def test_move(self, real_album_factory, tmp_config, tmp_path): """Test all items in the library are moved when the command is invoked.""" cli_args = ["move"] tmp_settings = f""" default_plugins = ["move"] [move] library_path = '''{tmp_path.resolve()}''' """ config = tmp_config(tmp_settings) config.init_db() album1 = real_album_factory() album2 = real_album_factory() with session_scope() as session: session.merge(album1) session.merge(album2) moe.cli.main(cli_args, config) with session_scope() as new_session: albums = new_session.execute(sa.select(Album)).scalars().all() for album in albums: assert tmp_path in album.path.parents for track in album.tracks: assert tmp_path in track.path.parents for extra in album.extras: assert tmp_path in extra.path.parents
def test_parse_args(self, real_track, tmp_path, tmp_config): """Music is removed from the library when the `remove` command is invoked.""" cli_args = ["remove", "*"] config = tmp_config(settings='default_plugins = ["remove"]') config.init_db() with session_scope() as session: session.add(real_track) moe.cli.main(cli_args, config) with session_scope() as session2: assert not session2.query(Track).scalar()
def test_add_album(self, real_album, tmp_config, tmp_path): """Albums are copied to `library_path` after they are added.""" cli_args = ["add", str(real_album.path)] tmp_settings = f""" default_plugins = ["add", "move"] [move] library_path = '''{tmp_path.resolve()}''' """ config = tmp_config(tmp_settings) og_paths = [track.path for track in real_album.tracks] og_paths += [extra.path for extra in real_album.extras] og_paths.append(real_album.path) for track in real_album.tracks: assert tmp_path not in track.path.parents for extra in real_album.extras: assert tmp_path not in extra.path.parents moe.cli.main(cli_args, config) with session_scope() as session: album = session.query(Album).one() assert tmp_path in album.path.parents assert album.path.exists() for new_track in album.tracks: assert tmp_path in new_track.path.parents assert new_track.path.exists() for new_extra in album.extras: assert tmp_path in new_extra.path.parents assert new_extra.path.exists() for og_path in og_paths: assert og_path.exists()
def test_write_through_flush(self, real_album, tmp_config): """If a flush occurs, ensure we still write all items that changed. A database "flush" will occur if querying, or if editing an association attribute. This test ensures we aren't just naively checking `session.dirty` to get a list of all edited items. """ new_genre = "new genre" cli_args = ["edit", "*", f"genre={new_genre}"] tmp_settings = """ default_plugins = ["edit", "write"] """ config = tmp_config(tmp_settings) config.init_db() og_paths = [track.path for track in real_album.tracks] with session_scope() as session: session.merge(real_album) moe.cli.main(cli_args, config) new_tracks = [Track.from_tags(path) for path in og_paths] for track in new_tracks: assert track.genre == new_genre
def test_prompt_choice(self, mock_mb_by_id, mock_mb_search, real_album, tmp_config): """We can search from user input.""" cli_args = ["add", str(real_album.path)] config = tmp_config( settings='default_plugins = ["add", "musicbrainz"]') mock_q = Mock() mock_q.ask.side_effect = [musicbrainz._enter_id, prompt._apply_changes] with patch("moe.plugins.add.prompt.questionary.rawselect", return_value=mock_q): mock_q = Mock() mock_q.ask.return_value = "new id" with patch("moe.plugins.add.prompt.questionary.text", return_value=mock_q): cli.main(cli_args, config) mock_mb_by_id.assert_called_with("new id", includes=musicbrainz.RELEASE_INCLUDES) with session_scope() as session: album = session.query(Album).one() assert album.artist == "Kanye West" assert album.date == datetime.date(2010, 11, 22) assert album.mb_album_id == "2fcfcaaa-6594-4291-b79f-2d354139e108" assert album.title == "My Beautiful Dark Twisted Fantasy"
def test_parse_args(self, real_track, tmp_config): """Music is edited when the `edit` command is invoked.""" new_title = "Lovely Day" assert real_track.title != new_title cli_args = ["edit", "*", f"title={new_title}"] config = tmp_config(settings='default_plugins = ["edit"]') config.init_db() with session_scope() as pre_edit_session: pre_edit_session.add(real_track) moe.cli.main(cli_args, config) with session_scope() as post_edit_session: edited_track = post_edit_session.query(Track).one() assert edited_track.title == "Lovely Day"
def test_file(self, real_track, tmp_config): """Tracks are added to the library when a file is passed to `add`.""" cli_args = ["add", str(real_track.path)] config = tmp_config(settings='default_plugins = ["add"]') moe.cli.main(cli_args, config) with session_scope() as session: assert session.query(Track).one()
def test_dup(self, mock_track_factory, tmp_session): """Duplicate tracks should raise a DbDupAlbumError.""" track1 = mock_track_factory() track2 = mock_track_factory() track2.track_num = track1.track_num with pytest.raises(DbDupAlbumError): with session_scope() as session: session.add(track1) session.add(track2)
def test_dup(self, mock_album_factory, tmp_session): """Duplicate albums should raise a DbDupAlbumError.""" album1 = mock_album_factory() album2 = mock_album_factory() album1.date = album2.date with pytest.raises(DbDupAlbumError): with session_scope() as session: session.add(album1) session.add(album2)
def test_parse_args(self, capsys, real_track, tmp_config): """A track's info is printed when the `info` command is invoked.""" cli_args = ["info", "*"] config = tmp_config(settings='default_plugins = ["info"]') config.init_db() with session_scope() as session: session.add(real_track) moe.cli.main(cli_args, config) assert capsys.readouterr().out
def test_parse_args(self, capsys, real_track, tmp_config): """Music is listed from the library when the `list` command is invoked.""" cli_args = ["list", "*"] config = tmp_config(settings='default_plugins = ["list"]') config.init_db() with session_scope() as session: session.add(real_track) moe.cli.main(cli_args, config) assert capsys.readouterr().out
def test_commit_on_systemexit(self, real_track, tmp_config): """If SystemExit intentionally raised, still commit the session.""" cli_args = ["add", "bad_file", str(real_track.path)] config = tmp_config(settings='default_plugins = ["add"]') with pytest.raises(SystemExit) as error: moe.cli.main(cli_args, config) assert error.value.code != 0 with session_scope() as session: session.query(Track).one()
def test_dup_path(self, mock_album_factory, tmp_session): """Duplicate albums can also be defined as having the same path. These should also raise the same DbDupTrackError. """ album1 = mock_album_factory() album2 = mock_album_factory() album2.path = album1.path with pytest.raises(DbDupAlbumError): with session_scope() as session: session.add(album1) session.add(album2)
def test_album(self, real_album, tmp_config): """Prompt is run with a plugin implementing the ``import_album`` hook.""" cli_args = ["add", str(real_album.path)] config = tmp_config(settings='default_plugins = ["add"]') config.plugin_manager.register(ImportPlugin) mock_q = Mock() mock_q.ask.return_value = add.prompt._apply_changes with patch("moe.plugins.add.prompt.questionary.rawselect", return_value=mock_q): moe.cli.main(cli_args, config) with session_scope() as session: album = session.query(Album).one() assert album.title == "pre-add plugin"
def tmp_session() -> Iterator[Session]: """Creates temporary Session instance for database interaction. The database is a temporary sqlite instance created in memory. Yields: session: temp Session instance """ engine = sqlalchemy.create_engine("sqlite:///:memory:") config = Config(config_dir=MagicMock()) config.init_db(engine=engine) with session_scope() as session: yield session
def test_dup_merge(self, real_album_factory, tmp_session): """Duplicate errors should not occur if the existing album is merged.""" album1 = real_album_factory() album2 = real_album_factory() album1.date = album2.date album1.path = album2.path album1.tracks.pop(0) album1.extras.pop(0) with session_scope() as session: session.add(album1) album2.merge(album2.get_existing(session)) session.merge(album2) db_album = session.query(Album).one() assert sorted(db_album.tracks) == sorted(album2.tracks) assert sorted(db_album.extras) == sorted(album2.extras)
def test_edit_track(self, real_track, tmp_config): """Any altered Tracks have their tags written.""" new_title = "Summertime" cli_args = ["edit", "*", f"title={new_title}"] tmp_settings = """ default_plugins = ["edit", "write"] """ config = tmp_config(tmp_settings) config.init_db() og_path = real_track.path with session_scope() as session: session.add(real_track) moe.cli.main(cli_args, config) new_track = Track.from_tags(og_path) assert new_track.title == new_title
def test_album(self, mock_mb_by_id, mock_mb_search, real_album, tmp_config): """We can import and add an album to the library.""" cli_args = ["add", str(real_album.path)] config = tmp_config( settings='default_plugins = ["add", "musicbrainz"]') mock_q = Mock() mock_q.ask.return_value = prompt._apply_changes with patch("moe.plugins.add.prompt.questionary.rawselect", return_value=mock_q): cli.main(cli_args, config) with session_scope() as session: album = session.query(Album).one() assert album.artist == "Kanye West" assert album.date == datetime.date(2010, 11, 22) assert album.mb_album_id == "2fcfcaaa-6594-4291-b79f-2d354139e108" assert album.title == "My Beautiful Dark Twisted Fantasy"
def _parse_args(args: List[str], config: Config): """Parses the commandline arguments. Args: args: Arguments to parse. Should not include 'moe'. config: User configuration for moe. Raises: SystemExit: No sub-commands given. Does not include root commands such as `--version` or `--help`. """ moe_parser = _create_arg_parser() # load all sub-commands cmd_parsers = moe_parser.add_subparsers(help="command to run", dest="command") config.plugin_manager.hook.add_command(cmd_parsers=cmd_parsers) parsed_args = moe_parser.parse_args(args) # no sub-command given if not parsed_args.command: moe_parser.print_help(sys.stderr) raise SystemExit(1) _set_root_log_lvl(parsed_args) # call the sub-command's handler within a single session config.init_db() with session_scope() as session: sqlalchemy.event.listen( session, "before_flush", functools.partial(_edit_new_items, config=config)) sqlalchemy.event.listen( session, "after_flush", functools.partial(_process_new_items, config=config)) parsed_args.func(config, session, args=parsed_args)