diff --git a/openlp/core/common/registry.py b/openlp/core/common/registry.py index 2d1bcd1e7..4cdca1882 100644 --- a/openlp/core/common/registry.py +++ b/openlp/core/common/registry.py @@ -38,6 +38,9 @@ class Registry(metaclass=Singleton): """ log.info('Registry loaded') + # Try to get around the AttributeError in tests + functions_list = {} + @classmethod def create(cls) -> 'Registry': """ diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 6a9662d2d..03ce8ce0d 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -30,7 +30,7 @@ from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.songs.forms.authorsform import AuthorsForm from openlp.plugins.songs.forms.songbookform import SongBookForm from openlp.plugins.songs.forms.topicsform import TopicsForm -from openlp.plugins.songs.lib.db import Author, SongBook, Song, Topic, SongBookEntry +from openlp.plugins.songs.lib.db import Author, AuthorSong, SongBook, Song, Topic, SongBookEntry from .songmaintenancedialog import Ui_SongMaintenanceDialog @@ -431,7 +431,13 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP ) ) # Find the songs, which have the old_author as author. - songs = self.manager.get_all_objects(Song, Song.authors.contains(old_author)) + songs = self.manager.get_all_objects( + Song, + and_( + Song.id == AuthorSong.song_id, + AuthorSong.author_id == old_author.id + ) + ) for song in songs: for author_song in song.authors_songs: song.add_author(existing_author, author_song.author_type) diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 000000000..8691cae57 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +filterwarnings = + ignore::UserWarning diff --git a/tests/openlp_plugins/songs/forms/test_songmaintenanceform.py b/tests/openlp_plugins/songs/forms/test_songmaintenanceform.py index 2e92125cd..c653ea340 100644 --- a/tests/openlp_plugins/songs/forms/test_songmaintenanceform.py +++ b/tests/openlp_plugins/songs/forms/test_songmaintenanceform.py @@ -30,94 +30,94 @@ from PyQt5 import QtCore, QtWidgets from openlp.core.common.i18n import UiStrings from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings from openlp.core.db.manager import DBManager -from openlp.plugins.songs.lib.db import init_schema, SongBook, Song, SongBookEntry +from openlp.plugins.songs.lib.db import init_schema, Author, AuthorSong, SongBook, Song, SongBookEntry from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm from sqlalchemy.sql import and_ @pytest.fixture() -def form_env(settings): +def db_manager() -> MagicMock(): + return MagicMock() + + +@pytest.fixture() +def maintenance_form(registry: Registry, settings: Settings, db_manager: MagicMock) -> SongMaintenanceForm: main_window = QtWidgets.QMainWindow() - Registry().register('main_window', main_window) - mocked_manager = MagicMock() - frm = SongMaintenanceForm(mocked_manager) - yield frm, mocked_manager + registry.register('main_window', main_window) + frm = SongMaintenanceForm(db_manager) + yield frm del frm del main_window -def test_constructor(form_env): +def test_constructor(db_manager: MagicMock, maintenance_form: SongMaintenanceForm): """ Test that a SongMaintenanceForm is created successfully """ # GIVEN: A SongMaintenanceForm - form = form_env[0] - mocked_manager = form_env[1] # WHEN: The form is created # THEN: It should have some of the right components - assert form is not None - assert form.manager is mocked_manager + assert maintenance_form is not None + assert maintenance_form.manager is db_manager -@patch.object(QtWidgets.QDialog, 'exec') -def test_exect(mocked_exec, form_env): +@patch('openlp.plugins.songs.forms.songmaintenanceform.QtWidgets.QDialog.exec') +def test_exec(mocked_exec: MagicMock, maintenance_form: SongMaintenanceForm): """ Test the song maintenance form being executed """ # GIVEN: A song maintenance form - form = form_env[0] mocked_exec.return_value = True # WHEN: The song mainetnance form is executed - with patch.object(form, 'type_list_widget') as mocked_type_list_widget, \ - patch.object(form, 'reset_authors') as mocked_reset_authors, \ - patch.object(form, 'reset_topics') as mocked_reset_topics, \ - patch.object(form, 'reset_song_books') as mocked_reset_song_books: - result = form.exec(from_song_edit=True) + with patch.object(maintenance_form, 'type_list_widget') as mocked_type_list_widget, \ + patch.object(maintenance_form, 'reset_authors') as mocked_reset_authors, \ + patch.object(maintenance_form, 'reset_topics') as mocked_reset_topics, \ + patch.object(maintenance_form, 'reset_song_books') as mocked_reset_song_books: + result = maintenance_form.exec(from_song_edit=True) # THEN: The correct methods should have been called - assert form.from_song_edit is True + assert maintenance_form.from_song_edit is True mocked_type_list_widget.setCurrentRow.assert_called_once_with(0) mocked_reset_authors.assert_called_once_with() mocked_reset_topics.assert_called_once_with() mocked_reset_song_books.assert_called_once_with() mocked_type_list_widget.setFocus.assert_called_once_with() - mocked_exec.assert_called_once_with(form) + mocked_exec.assert_called_once_with(maintenance_form) assert result is True -def test_get_current_item_id_no_item(form_env): +def test_get_current_item_id_no_item(maintenance_form: SongMaintenanceForm): """ Test _get_current_item_id() when there's no item """ # GIVEN: A song maintenance form without a selected item - form = form_env[0] mocked_list_widget = MagicMock() mocked_list_widget.currentItem.return_value = None # WHEN: _get_current_item_id() is called - result = form._get_current_item_id(mocked_list_widget) + result = maintenance_form._get_current_item_id(mocked_list_widget) # THEN: The result should be -1 mocked_list_widget.currentItem.assert_called_once_with() assert result == -1 -def test_get_current_item_id(form_env): +def test_get_current_item_id(maintenance_form: SongMaintenanceForm): """ Test _get_current_item_id() when there's a valid item """ # GIVEN: A song maintenance form with a selected item - form = form_env[0] mocked_item = MagicMock() mocked_item.data.return_value = 7 mocked_list_widget = MagicMock() mocked_list_widget.currentItem.return_value = mocked_item # WHEN: _get_current_item_id() is called - result = form._get_current_item_id(mocked_list_widget) + result = maintenance_form._get_current_item_id(mocked_list_widget) # THEN: The result should be -1 mocked_list_widget.currentItem.assert_called_once_with() @@ -126,13 +126,12 @@ def test_get_current_item_id(form_env): @patch('openlp.plugins.songs.forms.songmaintenanceform.critical_error_message_box') -def test_delete_item_no_item_id(mocked_critical_error_message_box, form_env): +def test_delete_item_no_item_id(mocked_critical_error_message_box: MagicMock, maintenance_form: SongMaintenanceForm): """ Test the _delete_item() method when there is no item selected """ # GIVEN: Some mocked items - form, manager = form_env - manager.get_object.return_value = None + maintenance_form.manager.get_object.return_value = None mocked_list_widget = MagicMock() mocked_reset_func = MagicMock() dialog_title = 'Delete Item' @@ -140,10 +139,10 @@ def test_delete_item_no_item_id(mocked_critical_error_message_box, form_env): error_text = 'There was a problem deleting this item' # WHEN: _delete_item() is called - with patch.object(form, '_get_current_item_id') as mocked_get_current_item_id: + with patch.object(maintenance_form, '_get_current_item_id') as mocked_get_current_item_id: mocked_get_current_item_id.return_value = -1 - form._delete_item(SongBook, mocked_list_widget, mocked_reset_func, dialog_title, delete_text, - error_text) + maintenance_form._delete_item(SongBook, mocked_list_widget, mocked_reset_func, dialog_title, delete_text, + error_text) # THEN: The right things should have been called mocked_get_current_item_id.assert_called_once_with(mocked_list_widget) @@ -151,13 +150,12 @@ def test_delete_item_no_item_id(mocked_critical_error_message_box, form_env): @patch('openlp.plugins.songs.forms.songmaintenanceform.critical_error_message_box') -def test_delete_item_invalid_item(mocked_critical_error_message_box, form_env): +def test_delete_item_invalid_item(mocked_critical_error_message_box: MagicMock, maintenance_form: SongMaintenanceForm): """ Test the _delete_item() method when the item doesn't exist in the database """ # GIVEN: Some mocked items - form, manager = form_env - manager.get_object.return_value = None + maintenance_form.manager.get_object.return_value = None mocked_list_widget = MagicMock() mocked_reset_func = MagicMock() dialog_title = 'Delete Item' @@ -165,28 +163,27 @@ def test_delete_item_invalid_item(mocked_critical_error_message_box, form_env): error_text = 'There was a problem deleting this item' # WHEN: _delete_item() is called - with patch.object(form, '_get_current_item_id') as mocked_get_current_item_id: + with patch.object(maintenance_form, '_get_current_item_id') as mocked_get_current_item_id: mocked_get_current_item_id.return_value = 1 - form._delete_item(SongBook, mocked_list_widget, mocked_reset_func, dialog_title, delete_text, - error_text) + maintenance_form._delete_item(SongBook, mocked_list_widget, mocked_reset_func, dialog_title, delete_text, + error_text) # THEN: The right things should have been called mocked_get_current_item_id.assert_called_once_with(mocked_list_widget) - manager.get_object.assert_called_once_with(SongBook, 1) + maintenance_form.manager.get_object.assert_called_once_with(SongBook, 1) mocked_critical_error_message_box.assert_called_once_with(dialog_title, error_text) @patch('openlp.plugins.songs.forms.songmaintenanceform.critical_error_message_box') -def test_delete_item(mocked_critical_error_message_box, form_env): +def test_delete_item(mocked_critical_error_message_box: MagicMock, maintenance_form: SongMaintenanceForm): """ Test the _delete_item() method """ # GIVEN: Some mocked items - form, mocked_manager = form_env mocked_item = MagicMock() mocked_item.songs = [] mocked_item.id = 1 - mocked_manager.get_object.return_value = mocked_item + maintenance_form.manager.get_object.return_value = mocked_item mocked_critical_error_message_box.return_value = QtWidgets.QMessageBox.Yes mocked_list_widget = MagicMock() mocked_reset_func = MagicMock() @@ -195,30 +192,29 @@ def test_delete_item(mocked_critical_error_message_box, form_env): error_text = 'There was a problem deleting this item' # WHEN: _delete_item() is called - with patch.object(form, '_get_current_item_id') as mocked_get_current_item_id: + with patch.object(maintenance_form, '_get_current_item_id') as mocked_get_current_item_id: mocked_get_current_item_id.return_value = 1 - form._delete_item(SongBook, mocked_list_widget, mocked_reset_func, dialog_title, delete_text, - error_text) + maintenance_form._delete_item(SongBook, mocked_list_widget, mocked_reset_func, dialog_title, delete_text, + error_text) # THEN: The right things should have been called mocked_get_current_item_id.assert_called_once_with(mocked_list_widget) - mocked_manager.get_object.assert_called_once_with(SongBook, 1) - mocked_critical_error_message_box.assert_called_once_with(dialog_title, delete_text, form, True) - mocked_manager.delete_object.assert_called_once_with(SongBook, 1) + maintenance_form.manager.get_object.assert_called_once_with(SongBook, 1) + mocked_critical_error_message_box.assert_called_once_with(dialog_title, delete_text, maintenance_form, True) + maintenance_form.manager.delete_object.assert_called_once_with(SongBook, 1) mocked_reset_func.assert_called_once_with() @patch('openlp.plugins.songs.forms.songmaintenanceform.critical_error_message_box') -def test_delete_book_assigned(mocked_critical_error_message_box, form_env): +def test_delete_book_assigned(mocked_critical_error_message_box: MagicMock, maintenance_form: SongMaintenanceForm): """ Test the _delete_item() method """ # GIVEN: Some mocked items - form, mocked_manager = form_env mocked_item = create_autospec(SongBook, spec_set=True) mocked_item.id = 1 mocked_item.songs = [MagicMock(title='Amazing Grace')] - mocked_manager.get_object.return_value = mocked_item + maintenance_form.manager.get_object.return_value = mocked_item mocked_critical_error_message_box.return_value = QtWidgets.QMessageBox.Yes mocked_list_widget = MagicMock() mocked_reset_func = MagicMock() @@ -227,28 +223,27 @@ def test_delete_book_assigned(mocked_critical_error_message_box, form_env): error_text = 'This book cannot be deleted, it is currenty assigned to at least one song.' # WHEN: _delete_item() is called - with patch.object(form, '_get_current_item_id') as mocked_get_current_item_id: + with patch.object(maintenance_form, '_get_current_item_id') as mocked_get_current_item_id: mocked_get_current_item_id.return_value = 1 - form._delete_item(SongBook, mocked_list_widget, mocked_reset_func, dialog_title, delete_text, - error_text) + maintenance_form._delete_item(SongBook, mocked_list_widget, mocked_reset_func, dialog_title, delete_text, + error_text) # THEN: The right things should have been called mocked_get_current_item_id.assert_called_once_with(mocked_list_widget) - mocked_manager.get_object.assert_called_once_with(SongBook, 1) + maintenance_form.manager.get_object.assert_called_once_with(SongBook, 1) mocked_critical_error_message_box.assert_called_once_with(dialog_title, error_text + '\n\nAmazing Grace') - mocked_manager.delete_object.assert_not_called() + maintenance_form.manager.delete_object.assert_not_called() mocked_reset_func.assert_not_called() @patch('openlp.plugins.songs.forms.songmaintenanceform.QtWidgets.QListWidgetItem') @patch('openlp.plugins.songs.forms.songmaintenanceform.Author') -def test_reset_authors(MockedAuthor, MockedQListWidgetItem, form_env): +def test_reset_authors(MockedAuthor: MagicMock, MockedQListWidgetItem: MagicMock, + maintenance_form: SongMaintenanceForm): """ Test the reset_authors() method """ # GIVEN: A mocked authors_list_widget and a few other mocks - form = form_env[0] - mocked_manager = form_env[1] mocked_author1 = MagicMock() mocked_author1.display_name = 'John Newton' mocked_author1.id = 1 @@ -262,16 +257,16 @@ def test_reset_authors(MockedAuthor, MockedQListWidgetItem, form_env): mocked_author_item2 = MagicMock() MockedQListWidgetItem.side_effect = [mocked_author_item1, mocked_author_item2] MockedAuthor.display_name = None - mocked_manager.get_all_objects.return_value = mocked_authors + maintenance_form.manager.get_all_objects.return_value = mocked_authors # WHEN: reset_authors() is called - with patch.object(form, 'authors_list_widget') as mocked_authors_list_widget: - form.reset_authors() + with patch.object(maintenance_form, 'authors_list_widget') as mocked_authors_list_widget: + maintenance_form.reset_authors() # THEN: The authors list should be reset expected_widget_item_calls = [call('John Wesley'), call('John Newton')] mocked_authors_list_widget.clear.assert_called_once_with() - mocked_manager.get_all_objects.assert_called_once_with(MockedAuthor) + maintenance_form.manager.get_all_objects.assert_called_once_with(MockedAuthor) # Do not care which order items are called since the order is different on macos vs others MockedQListWidgetItem.assert_has_calls(expected_widget_item_calls, any_order=True) mocked_author_item1.setData.assert_called_once_with(QtCore.Qt.UserRole, ANY) @@ -282,28 +277,26 @@ def test_reset_authors(MockedAuthor, MockedQListWidgetItem, form_env): @patch('openlp.plugins.songs.forms.songmaintenanceform.QtWidgets.QListWidgetItem') @patch('openlp.plugins.songs.forms.songmaintenanceform.Topic') -def test_reset_topics(MockedTopic, MockedQListWidgetItem, form_env): +def test_reset_topics(MockedTopic: MagicMock, MockedQListWidgetItem: MagicMock, maintenance_form: SongMaintenanceForm): """ Test the reset_topics() method """ # GIVEN: Some mocked out objects and methods - form = form_env[0] - mocked_manager = form_env[1] MockedTopic.name = 'Grace' mocked_topic = MagicMock() mocked_topic.id = 1 mocked_topic.name = 'Grace' - mocked_manager.get_all_objects.return_value = [mocked_topic] + maintenance_form.manager.get_all_objects.return_value = [mocked_topic] mocked_topic_item = MagicMock() MockedQListWidgetItem.return_value = mocked_topic_item # WHEN: reset_topics() is called - with patch.object(form, 'topics_list_widget') as mocked_topic_list_widget: - form.reset_topics() + with patch.object(maintenance_form, 'topics_list_widget') as mocked_topic_list_widget: + maintenance_form.reset_topics() # THEN: The topics list should be reset correctly mocked_topic_list_widget.clear.assert_called_once_with() - mocked_manager.get_all_objects.assert_called_once_with(MockedTopic) + maintenance_form.manager.get_all_objects.assert_called_once_with(MockedTopic) MockedQListWidgetItem.assert_called_once_with('Grace') mocked_topic_item.setData.assert_called_once_with(QtCore.Qt.UserRole, 1) mocked_topic_list_widget.addItem.assert_called_once_with(mocked_topic_item) @@ -311,29 +304,28 @@ def test_reset_topics(MockedTopic, MockedQListWidgetItem, form_env): @patch('openlp.plugins.songs.forms.songmaintenanceform.QtWidgets.QListWidgetItem') @patch('openlp.plugins.songs.forms.songmaintenanceform.SongBook') -def test_reset_song_books(MockedBook, MockedQListWidgetItem, form_env): +def test_reset_song_books(MockedBook: MagicMock, MockedQListWidgetItem: MagicMock, + maintenance_form: SongMaintenanceForm): """ Test the reset_song_books() method """ # GIVEN: Some mocked out objects and methods - form = form_env[0] - mocked_manager = form_env[1] MockedBook.name = 'Hymnal' mocked_song_book = MagicMock() mocked_song_book.id = 1 mocked_song_book.name = 'Hymnal' mocked_song_book.publisher = 'Hymns and Psalms, Inc.' - mocked_manager.get_all_objects.return_value = [mocked_song_book] + maintenance_form.manager.get_all_objects.return_value = [mocked_song_book] mocked_song_book_item = MagicMock() MockedQListWidgetItem.return_value = mocked_song_book_item # WHEN: reset_song_books() is called - with patch.object(form, 'song_books_list_widget') as mocked_song_book_list_widget: - form.reset_song_books() + with patch.object(maintenance_form, 'song_books_list_widget') as mocked_song_book_list_widget: + maintenance_form.reset_song_books() # THEN: The song_books list should be reset correctly mocked_song_book_list_widget.clear.assert_called_once_with() - mocked_manager.get_all_objects.assert_called_once_with(MockedBook) + maintenance_form.manager.get_all_objects.assert_called_once_with(MockedBook) MockedQListWidgetItem.assert_called_once_with('Hymnal (Hymns and Psalms, Inc.)') mocked_song_book_item.setData.assert_called_once_with(QtCore.Qt.UserRole, 1) mocked_song_book_list_widget.addItem.assert_called_once_with(mocked_song_book_item) @@ -341,13 +333,11 @@ def test_reset_song_books(MockedBook, MockedQListWidgetItem, form_env): @patch('openlp.plugins.songs.forms.songmaintenanceform.and_') @patch('openlp.plugins.songs.forms.songmaintenanceform.Author') -def test_check_author_exists(MockedAuthor, mocked_and, form_env): +def test_check_author_exists(MockedAuthor: MagicMock, mocked_and: MagicMock, maintenance_form: SongMaintenanceForm): """ Test the check_author_exists() method """ # GIVEN: A bunch of mocked out stuff - form = form_env[0] - mocked_manager = form_env[1] MockedAuthor.first_name = 'John' MockedAuthor.last_name = 'Newton' MockedAuthor.display_name = 'John Newton' @@ -357,54 +347,50 @@ def test_check_author_exists(MockedAuthor, mocked_and, form_env): mocked_new_author.display_name = 'John Newton' mocked_and.return_value = True mocked_authors = [MagicMock(), MagicMock()] - mocked_manager.get_all_objects.return_value = mocked_authors + maintenance_form.manager.get_all_objects.return_value = mocked_authors # WHEN: check_author_exists() is called - with patch.object(form, '_check_object_exists') as mocked_check_object_exists: + with patch.object(maintenance_form, '_check_object_exists') as mocked_check_object_exists: mocked_check_object_exists.return_value = True - result = form.check_author_exists(mocked_new_author, edit=True) + result = maintenance_form.check_author_exists(mocked_new_author, edit=True) # THEN: The correct result is returned mocked_and.assert_called_once_with(True, True, True) - mocked_manager.get_all_objects.assert_called_once_with(MockedAuthor, True) + maintenance_form.manager.get_all_objects.assert_called_once_with(MockedAuthor, True) mocked_check_object_exists.assert_called_once_with(mocked_authors, mocked_new_author, True) assert result is True @patch('openlp.plugins.songs.forms.songmaintenanceform.Topic') -def test_check_topic_exists(MockedTopic, form_env): +def test_check_topic_exists(MockedTopic: MagicMock, maintenance_form: SongMaintenanceForm): """ Test the check_topic_exists() method """ # GIVEN: Some mocked stuff - form = form_env[0] - mocked_manager = form_env[1] MockedTopic.name = 'Grace' mocked_new_topic = MagicMock() mocked_new_topic.name = 'Grace' mocked_topics = [MagicMock(), MagicMock()] - mocked_manager.get_all_objects.return_value = mocked_topics + maintenance_form.manager.get_all_objects.return_value = mocked_topics # WHEN: check_topic_exists() is run - with patch.object(form, '_check_object_exists') as mocked_check_object_exists: + with patch.object(maintenance_form, '_check_object_exists') as mocked_check_object_exists: mocked_check_object_exists.return_value = True - result = form.check_topic_exists(mocked_new_topic, True) + result = maintenance_form.check_topic_exists(mocked_new_topic, True) # THEN: The correct things should have been called - mocked_manager.get_all_objects.assert_called_once_with(MockedTopic, True) + maintenance_form.manager.get_all_objects.assert_called_once_with(MockedTopic, True) mocked_check_object_exists.assert_called_once_with(mocked_topics, mocked_new_topic, True) assert result is True @patch('openlp.plugins.songs.forms.songmaintenanceform.and_') @patch('openlp.plugins.songs.forms.songmaintenanceform.SongBook') -def test_check_song_book_exists(MockedBook, mocked_and, form_env): +def test_check_song_book_exists(MockedBook: MagicMock, mocked_and: MagicMock, maintenance_form): """ Test the check_song_book_exists() method """ # GIVEN: Some mocked stuff - form = form_env[0] - mocked_manager = form_env[1] MockedBook.name = 'Hymns' MockedBook.publisher = 'Christian Songs' mocked_new_book = MagicMock() @@ -412,84 +398,83 @@ def test_check_song_book_exists(MockedBook, mocked_and, form_env): mocked_new_book.publisher = 'Christian Songs' mocked_and.return_value = True mocked_books = [MagicMock(), MagicMock()] - mocked_manager.get_all_objects.return_value = mocked_books + maintenance_form.manager.get_all_objects.return_value = mocked_books # WHEN: check_book_exists() is run - with patch.object(form, '_check_object_exists') as mocked_check_object_exists: + with patch.object(maintenance_form, '_check_object_exists') as mocked_check_object_exists: mocked_check_object_exists.return_value = True - result = form.check_song_book_exists(mocked_new_book, True) + result = maintenance_form.check_song_book_exists(mocked_new_book, True) # THEN: The correct things should have been called mocked_and.assert_called_once_with(True, True) - mocked_manager.get_all_objects.assert_called_once_with(MockedBook, True) + maintenance_form.manager.get_all_objects.assert_called_once_with(MockedBook, True) mocked_check_object_exists.assert_called_once_with(mocked_books, mocked_new_book, True) assert result is True -def test_check_object_exists_no_existing_objects(form_env): +def test_check_object_exists_no_existing_objects(maintenance_form: SongMaintenanceForm): """ Test the _check_object_exists() method when there are no existing objects """ # GIVEN: A SongMaintenanceForm instance # WHEN: _check_object_exists() is called without existing objects - form = form_env[0] - result = form._check_object_exists([], None, False) - # THEN: The result should be True - assert result is True + assert maintenance_form._check_object_exists([], None, False) is True -def test_check_object_exists_without_edit(form_env): +def test_check_object_exists_without_edit(maintenance_form: SongMaintenanceForm): """ Test the _check_object_exists() method when edit is false """ # GIVEN: A SongMaintenanceForm instance # WHEN: _check_object_exists() is called with edit set to false - form = form_env[0] - result = form._check_object_exists([MagicMock()], None, False) - # THEN: The result should be False - assert result is False + assert maintenance_form._check_object_exists([MagicMock()], None, False) is False -def test_check_object_exists_not_found(form_env): +def test_check_object_exists_not_found(maintenance_form: SongMaintenanceForm): """ Test the _check_object_exists() method when the object is not found """ # GIVEN: A SongMaintenanceForm instance and some mocked objects - form = form_env[0] mocked_existing_objects = [MagicMock(id=1)] mocked_new_object = MagicMock(id=2) # WHEN: _check_object_exists() is called with edit set to false - result = form._check_object_exists(mocked_existing_objects, mocked_new_object, True) - # THEN: The result should be False - assert result is False + assert maintenance_form._check_object_exists(mocked_existing_objects, mocked_new_object, True) is False -def test_check_object_exists(form_env): +def test_check_object_exists(maintenance_form: SongMaintenanceForm): """ Test the _check_object_exists() method """ # GIVEN: A SongMaintenanceForm instance and some mocked objects - form = form_env[0] mocked_existing_objects = [MagicMock(id=1)] mocked_new_object = MagicMock(id=1) # WHEN: _check_object_exists() is called with edit set to false - result = form._check_object_exists(mocked_existing_objects, mocked_new_object, True) - # THEN: The result should be False - assert result is True + assert maintenance_form._check_object_exists(mocked_existing_objects, mocked_new_object, True) is True -def test_merge_song_books(registry, settings, temp_folder): +def test_merge_song_books(request, registry: Registry, settings: Settings, temp_folder: str): """ Test the functionality of merging 2 song books. """ # GIVEN a test database populated with test data, and a song maintenance form - db_tmp_path = os.path.join(temp_folder, 'test-songs-2.9.2.sqlite') + db_tmp_path = os.path.join(temp_folder, 'test-song-maint-merge-songbooks.sqlite') + + def cleanup_db(): + try: + os.unlink(db_tmp_path) + except Exception: + # Ignore exceptions + pass + + request.addfinalizer(cleanup_db) + + # Create a db manager manager = DBManager('songs', init_schema, db_file_path=db_tmp_path) # create 2 song books, both with the same name @@ -567,3 +552,88 @@ def test_merge_song_books(registry, settings, temp_folder): assert len(song3_book2_entry) == 1 # entry field should be overridden, as it was not set previously assert song3_book2_entry[0].entry == '40' + + +def test_merge_authors(request, registry: Registry, settings: Settings, temp_folder: str): + """ + Test merging two authors + """ + # GIVEN a test database populated with test data, and a song maintenance form + db_tmp_path = os.path.join(temp_folder, 'test-song-maint-merge-authors.sqlite') + print(db_tmp_path) + + # def cleanup_db(): + # try: + # os.unlink(db_tmp_path) + # except Exception: + # # Ignore exceptions + # pass + + # request.addfinalizer(cleanup_db) + + # Create a db manager + manager = DBManager('songs', init_schema, db_file_path=db_tmp_path) + + # create 2 song books, both with the same name + author1 = Author(first_name='John', last_name='Newton', display_name='John Newton') + manager.save_object(author1) + author2 = Author(first_name='John', last_name='Newton', display_name='John Newton') + manager.save_object(author2) + + # create 3 songs, all with same search_title + song1 = Song() + song1.title = 'test song1' + song1.lyrics = 'lyrics1' + song1.search_title = 'test song' + song1.search_lyrics = 'lyrics1' + manager.save_object(song1) + song2 = Song() + song2.title = 'test song2' + song2.lyrics = 'lyrics2' + song2.search_title = 'test song' + song2.search_lyrics = 'lyrics2' + manager.save_object(song2) + song3 = Song() + song3.title = 'test song3' + song3.lyrics = 'lyrics3' + song3.search_title = 'test song' + song3.search_lyrics = 'lyrics3' + manager.save_object(song3) + + # associate songs with authors + song1.add_author(author1) + song2.add_author(author1) + song3.add_author(author2) + + manager.save_objects([song1, song2, song3]) + + song_maintenance_form = SongMaintenanceForm(manager) + + # WHEN the song books are merged, getting rid of book1 + song_maintenance_form.merge_authors(author1) + + # THEN the database should reflect correctly the merge + + songs = manager.get_all_objects(Song, Song.search_title == 'test song') + author_song1 = manager.get_all_objects(AuthorSong, AuthorSong.author_id == author1.id) + author_song2 = manager.get_all_objects(AuthorSong, AuthorSong.author_id == author2.id) + song1_author2 = manager.get_all_objects(AuthorSong, and_(AuthorSong.author_id == author2.id, + AuthorSong.song_id == song1.id)) + song2_author2 = manager.get_all_objects(AuthorSong, and_(AuthorSong.author_id == author2.id, + AuthorSong.song_id == song2.id)) + song3_author2 = manager.get_all_objects(AuthorSong, and_(AuthorSong.author_id == author2.id, + AuthorSong.song_id == song3.id)) + authors = manager.get_all_objects(Author, Author.display_name == 'John Newton') + + # song records should not be deleted + assert len(songs) == 3 + # the old author should have been deleted + assert len(authors) == 1 + # there should not be any songs associated with this author + assert len(author_song1) == 0 + # each of the 3 songs should be associated with author2 + assert len(author_song2) == 3 + # the individual AuthorSong records should be correct + assert len(song1_author2) == 1 + assert len(song2_author2) == 1 + assert len(song3_author2) == 1