class TestTorrentDetailsCmd(CommandTestCase):
    def setUp(self):
        super().setUp()
        self.patch('stig.objects',
                   srvapi=self.srvapi)
        self.mock_display_details = MagicMock()
        self.patch('stig.commands.cli.TorrentDetailsCmd',
                   select_torrents=mock_select_torrents,
                   display_details=self.mock_display_details)

    async def do(self, args, tlist, success_exp, msgs=(), errors=()):
        self.srvapi.torrent.response = Response(success=success_exp, torrents=tlist, errors=errors)

        process = await self.execute(TorrentDetailsCmd, *args)
        self.assertEqual(process.success, success_exp)

        self.assert_stdout()
        self.assert_stderr(*tuple('^%s: %s$' % (TorrentDetailsCmd.name, err) for err in errors))

        self.srvapi.torrent.assert_called(1, 'torrents', (process.mock_tfilter,), {'keys': ('id', 'name')})

    async def test_no_match(self):
        tlist = ()
        await self.do(['mock filter'], tlist=tlist, success_exp=False, errors=('Mock error',))
        self.mock_display_details.assert_not_called()

    async def test_single_match(self):
        tlist = (MockTorrent(id=1, name='Torrent A', seeds='50'),)
        await self.do(['mock filter'], tlist=tlist, success_exp=True, errors=())
        self.mock_display_details.assert_called_once_with(1)

    async def test_multiple_matches_are_sorted_by_name(self):
        tlist = (MockTorrent(id=1, name='Torrent B', seeds='51'),
                 MockTorrent(id=2, name='Torrent A', seeds='50'))
        await self.do(['mock filter'], tlist=tlist, success_exp=True, errors=())
        self.mock_display_details.assert_called_once_with(2)

    @patch('stig.completion.candidates.torrent_filter')
    async def test_completion_candidates_for_posargs(self, mock_torrent_filter):
        mock_torrent_filter.return_value = Candidates(('a', 'b', 'c'))
        await self.assert_completion_candidates(TorrentDetailsCmd, Args(('details', 'foo'), curarg_index=1),
                                          exp_cands=('a', 'b', 'c'))
        mock_torrent_filter.assert_called_once_with('foo')
        mock_torrent_filter.reset_mock()
        await self.assert_completion_candidates(TorrentDetailsCmd, Args(('details', 'foo', 'bar'), curarg_index=2),
                                          exp_cands=None)
        mock_torrent_filter.assert_not_called()
class TestRenameCmd(CommandTestCase):
    def setUp(self):
        super().setUp()
        self.mock_get_relative_path_from_focused = MagicMock()
        self.mock_select_torrents = MagicMock()
        self.mock_srvapi = MagicMock()
        self.mock_srvapi.torrent.torrents = CoroutineMock()
        self.mock_srvapi.torrent.rename = CoroutineMock()
        self.patch('stig.objects',
                   srvapi=self.mock_srvapi)
        self.patch('stig.commands.cli.RenameCmd',
                   select_torrents=self.mock_select_torrents,
                   get_relative_path_from_focused=self.mock_get_relative_path_from_focused)

    async def test_discovering_focused_torrent(self):
        from stig.commands.cli import RenameCmd
        self.mock_get_relative_path_from_focused.return_value = None
        self.mock_select_torrents.return_value = 'mock filter'
        self.mock_srvapi.torrent.torrents.return_value = Response(
            success=True,
            torrents=(MockTorrent(id=1234, name='Some Torrent'),))

        info_cb, err_cb = MagicMock(), MagicMock()
        process = RenameCmd(['New Name'], info_handler=info_cb, error_handler=err_cb, loop=self.loop)
        await process.wait_async()
        self.assertEqual(process.success, True)
        info_cb.assert_not_called()
        err_cb.assert_not_called()

        self.mock_get_relative_path_from_focused.assert_called_once_with(unique=False)
        self.mock_select_torrents.assert_called_once_with(None, allow_no_filter=False, discover_torrent=True)
        self.mock_srvapi.torrent.torrents.assert_called_once_with('mock filter', keys=('id',))
        self.mock_srvapi.torrent.rename.assert_called_once_with(1234, path=None, new_name='New Name')

    async def test_discovering_focused_file(self):
        from stig.commands.cli import RenameCmd
        self.mock_get_relative_path_from_focused.return_value = 'id=1234/mock/path/to/file'
        self.mock_select_torrents.return_value = 'mock filter'
        self.mock_srvapi.torrent.torrents.return_value = Response(
            success=True,
            torrents=(MockTorrent(id=1234, name='Some Torrent'),))

        info_cb, err_cb = MagicMock(), MagicMock()
        process = RenameCmd(['file2'], info_handler=info_cb, error_handler=err_cb, loop=self.loop)
        await process.wait_async()
        self.assertEqual(process.success, True)
        info_cb.assert_not_called()
        err_cb.assert_not_called()

        self.mock_get_relative_path_from_focused.assert_called_once_with(unique=False)
        self.mock_select_torrents.assert_called_once_with('id=1234', allow_no_filter=False, discover_torrent=True)
        self.mock_srvapi.torrent.torrents.assert_called_once_with('mock filter', keys=('id',))
        self.mock_srvapi.torrent.rename.assert_called_once_with(1234, path='mock/path/to/file', new_name='file2')

    async def test_specifying_torrent(self):
        from stig.commands.cli import RenameCmd
        self.mock_select_torrents.return_value = 'mock filter'
        self.mock_srvapi.torrent.torrents.return_value = Response(
            success=True,
            torrents=(MockTorrent(id=1234, name='Some Torrent'),))

        info_cb, err_cb = MagicMock(), MagicMock()
        process = RenameCmd(['id=1234', 'Renamed Torrent'], info_handler=info_cb, error_handler=err_cb, loop=self.loop)
        await process.wait_async()
        self.assertEqual(process.success, True)
        info_cb.assert_not_called()
        err_cb.assert_not_called()

        self.mock_get_relative_path_from_focused.assert_not_called()
        self.mock_select_torrents.assert_called_once_with('id=1234', allow_no_filter=False, discover_torrent=True)
        self.mock_srvapi.torrent.torrents.assert_called_once_with('mock filter', keys=('id',))
        self.mock_srvapi.torrent.rename.assert_called_once_with(1234, path=None, new_name='Renamed Torrent')

    async def test_specifying_file(self):
        from stig.commands.cli import RenameCmd
        self.mock_select_torrents.return_value = 'mock filter'
        self.mock_srvapi.torrent.torrents.return_value = Response(
            success=True,
            torrents=(MockTorrent(id=1234, name='Some Torrent'),))

        info_cb, err_cb = MagicMock(), MagicMock()
        process = RenameCmd(['id=1234/mock/path/to/file', 'file2'], info_handler=info_cb, error_handler=err_cb, loop=self.loop)
        await process.wait_async()
        self.assertEqual(process.success, True)
        info_cb.assert_not_called()
        err_cb.assert_not_called()

        self.mock_get_relative_path_from_focused.assert_not_called()
        self.mock_select_torrents.assert_called_once_with('id=1234', allow_no_filter=False, discover_torrent=True)
        self.mock_srvapi.torrent.torrents.assert_called_once_with('mock filter', keys=('id',))
        self.mock_srvapi.torrent.rename.assert_called_once_with(1234, path='mock/path/to/file', new_name='file2')

    async def test_renaming_multiple_torrents_fails(self):
        from stig.commands.cli import RenameCmd
        self.mock_select_torrents.return_value = 'mock filter'
        self.mock_srvapi.torrent.torrents.return_value = Response(
            success=True,
            torrents=(MockTorrent(id=1234, name='Some Torrent'),
                      MockTorrent(id=1235, name='Some Torrent')))

        info_cb, err_cb = MagicMock(), MagicMock()
        process = RenameCmd(['Some Torrent', 'Renamed Torrent'], info_handler=info_cb, error_handler=err_cb, loop=self.loop)
        await process.wait_async()
        self.assertEqual(process.success, False)
        info_cb.assert_not_called()
        err_cb.assert_called_once_with('rename: mock filter matches more than one torrent')

        self.mock_get_relative_path_from_focused.assert_not_called()
        self.mock_select_torrents.assert_called_once_with('Some Torrent', allow_no_filter=False, discover_torrent=True)
        self.mock_srvapi.torrent.torrents.assert_called_once_with('mock filter', keys=('id',))
        self.mock_srvapi.torrent.rename.assert_not_called()

    async def test_renaming_files_of_multiple_torrents_succeeds(self):
        from stig.commands.cli import RenameCmd
        self.mock_select_torrents.return_value = 'mock filter'
        self.mock_srvapi.torrent.torrents.return_value = Response(
            success=True,
            torrents=(MockTorrent(id=1234, name='Some Torrent'),
                      MockTorrent(id=1235, name='Some Torrent')))

        info_cb, err_cb = MagicMock(), MagicMock()
        process = RenameCmd(['Some Torrent/mock/path/to/file', 'file2'], info_handler=info_cb, error_handler=err_cb, loop=self.loop)
        await process.wait_async()
        self.assertEqual(process.success, True)
        info_cb.assert_not_called()
        err_cb.assert_not_called()

        self.mock_get_relative_path_from_focused.assert_not_called()
        self.mock_select_torrents.assert_called_once_with('Some Torrent', allow_no_filter=False, discover_torrent=True)
        self.mock_srvapi.torrent.torrents.assert_called_once_with('mock filter', keys=('id',))
        self.assertEqual(self.mock_srvapi.torrent.rename.call_args_list,
                         [call(1234, path='mock/path/to/file', new_name='file2'),
                          call(1235, path='mock/path/to/file', new_name='file2')])

    async def test_TUI_renaming_files_of_multiple_torrents_disabled(self):
        from stig.commands.cli import RenameCmd
        self.mock_get_relative_path_from_focused.return_value = 'id=1235/focused/file'
        self.mock_select_torrents.return_value = 'mock filter'
        self.mock_srvapi.torrent.torrents.return_value = Response(
            success=True,
            torrents=(MockTorrent(id=1235, name='Some Torrent'),))

        info_cb, err_cb = MagicMock(), MagicMock()
        process = RenameCmd(['--unique', 'file2'],
                            info_handler=info_cb, error_handler=err_cb, loop=self.loop)
        await process.wait_async()
        self.assertEqual(process.success, True)
        info_cb.assert_not_called()
        err_cb.assert_not_called()

        self.mock_get_relative_path_from_focused.assert_called_once_with(unique=True)
        self.mock_select_torrents.assert_called_once_with('id=1235', allow_no_filter=False, discover_torrent=True)
        self.mock_srvapi.torrent.torrents.assert_called_once_with('mock filter', keys=('id',))
        self.assertEqual(self.mock_srvapi.torrent.rename.call_args_list,
                         [call(1235, path='focused/file', new_name='file2')])

    async def test_CLI_renaming_files_of_multiple_torrents_disabled_succeeds(self):
        from stig.commands.cli import RenameCmd
        self.mock_select_torrents.return_value = 'mock filter'
        self.mock_srvapi.torrent.torrents.return_value = Response(
            success=True,
            torrents=(MockTorrent(id=1235, name='Some Torrent'),))

        info_cb, err_cb = MagicMock(), MagicMock()
        process = RenameCmd(['--unique', 'id=1235/path/to/file', 'file2'],
                            info_handler=info_cb, error_handler=err_cb, loop=self.loop)
        await process.wait_async()
        self.assertEqual(process.success, True)
        info_cb.assert_not_called()
        err_cb.assert_not_called()

        self.mock_get_relative_path_from_focused.assert_not_called()
        self.mock_select_torrents.assert_called_once_with('id=1235', allow_no_filter=False, discover_torrent=True)
        self.mock_srvapi.torrent.torrents.assert_called_once_with('mock filter', keys=('id',))
        self.assertEqual(self.mock_srvapi.torrent.rename.call_args_list,
                         [call(1235, path='path/to/file', new_name='file2')])

    async def test_CLI_renaming_files_of_multiple_torrents_disabled_fails(self):
        from stig.commands.cli import RenameCmd
        self.mock_select_torrents.return_value = 'mock filter'
        self.mock_srvapi.torrent.torrents.return_value = Response(
            success=True,
            torrents=(MockTorrent(id=1234, name='Some Torrent'),
                      MockTorrent(id=1235, name='Some Torrent')))

        info_cb, err_cb = MagicMock(), MagicMock()
        process = RenameCmd(['--unique', 'Some Torrent/path/to/file', 'file2'],
                            info_handler=info_cb, error_handler=err_cb, loop=self.loop)
        await process.wait_async()
        self.assertEqual(process.success, False)
        info_cb.assert_not_called()
        err_cb.assert_called_once_with('rename: mock filter matches more than one torrent')

        self.mock_get_relative_path_from_focused.assert_not_called()
        self.mock_select_torrents.assert_called_once_with('Some Torrent', allow_no_filter=False, discover_torrent=True)
        self.mock_srvapi.torrent.torrents.assert_called_once_with('mock filter', keys=('id',))
        self.assertEqual(self.mock_srvapi.torrent.rename.call_args_list, [])

    async def test_discovering_fails(self):
        from stig.commands.cli import RenameCmd
        self.mock_get_relative_path_from_focused.return_value = None
        self.mock_select_torrents.side_effect = ValueError('No torrent given')

        info_cb, err_cb = MagicMock(), MagicMock()
        process = RenameCmd(['New Name'], info_handler=info_cb, error_handler=err_cb, loop=self.loop)
        await process.wait_async()
        self.assertEqual(process.success, False)
        info_cb.assert_not_called()
        err_cb.assert_called_once_with('rename: No torrent given')

        self.mock_get_relative_path_from_focused.assert_called_once_with(unique=False)
        self.mock_select_torrents.assert_called_once_with(None, allow_no_filter=False, discover_torrent=True)
        self.mock_srvapi.torrent.torrents.assert_not_called()
        self.mock_srvapi.torrent.rename.assert_not_called()