def test_should_retry_once_with_backoff(self):
        self.config.retry = 1
        self.callback.side_effect = self.throw_errors(1)
        resiliently = Resiliently(self.config)
        resiliently.call(self.callback, 'a', b='b')

        self.callback.assert_has_calls_exactly(
            [call('a', b='b'), call('a', b='b')])
    def test_should_fail_once_retry_exceeded(self):
        self.config.retry = 2
        self.callback.side_effect = self.throw_errors(3)
        resiliently = Resiliently(self.config)

        with pytest.raises(URLError):
            resiliently.call(self.callback, 'a', b='b')

        self.callback.assert_has_calls_exactly(
            [call('a', b='b'),
             call('a', b='b'),
             call('a', b='b')])
    def test_should_retry_specified_times(self):
        self.config.retry = 3
        self.callback.side_effect = self.throw_errors(3)
        resiliently = Resiliently(self.config)
        resiliently.call(self.callback, 'a', b='b')

        self.callback.assert_has_calls_exactly([
            call('a', b='b'),
            call('a', b='b'),
            call('a', b='b'),
            call('a', b='b')
        ])
        assert self.mock_sleep.call_count == 3, \
            f"Expected call_count of 3, was {self.mock_sleep.call_count}. Recieved {self.mock_sleep.call_args_list}"
    def test_list_folders_should_return_nothing_given_there_are_no_folders(
            self):
        self.user.getPhotosets.return_value = []
        storage = FlickrStorage(self.config, Resiliently(self.config))
        folders = list(storage.list_folders())

        assert not folders
    def test_list_folders_should_return_folders_given_there_are_folders(
            self, folders_fixture):
        self.user.getPhotosets.return_value = folders_fixture
        storage = FlickrStorage(self.config, Resiliently(self.config))
        folders = list(storage.list_folders())

        assert len(folders) == 2
    def test_list_files_should_list_files_when_root_folder_is_passed(
            self, files_fixture):
        self.user.getNotInSetPhotos.return_value = files_fixture
        storage = FlickrStorage(self.config, Resiliently(self.config))
        folder = RootFolder()
        files = list(storage.list_files(folder))

        assert len(files) == 2
    def test_upload_should_create_folder_given_it_doesnt_exist(self):
        self.user.getPhotosets.return_value = []
        self.mock_flickr_api.Photoset.create.return_value = MagicMock()
        storage = FlickrStorage(self.config, Resiliently(self.config))
        _ = list(storage.list_folders())
        storage.upload('/', 'new', 'micky.jpg', None)

        self.mock_flickr_api.Photoset.create.assert_called_once()
    def test_list_folders_should_not_list_folder_given_its_not_included(
            self, folders_fixture):
        self.config.include_dir = 'Folder 1'
        self.user.getPhotosets.return_value = folders_fixture
        storage = FlickrStorage(self.config, Resiliently(self.config))
        folders = list(storage.list_folders())

        assert len(folders) == 1
        assert folders[0].name == 'Folder 1'
    def test_list_files_should_not_list_files_given_there_are_no_files(
            self, folders_fixture):
        self.user.getPhotosets.return_value = folders_fixture
        folders_fixture[0].getPhotos.return_value = []
        storage = FlickrStorage(self.config, Resiliently(self.config))
        folders = list(storage.list_folders())
        files = list(storage.list_files(folders[0]))

        assert not files
    def test_upload_should_not_create_folder_given_it_exists(
            self, folders_fixture):
        self.user.getPhotosets.return_value = folders_fixture
        folders_fixture[0].addPhoto = MagicMock()
        storage = FlickrStorage(self.config, Resiliently(self.config))
        folders = list(storage.list_folders())
        storage.upload('/', folders[0].name, 'micky.jpg', None)

        self.mock_flickr_api.Photoset.create.assert_not_called()
        folders_fixture[0].addPhoto.assert_called_once()
    def test_list_files_should_not_list_file_given_its_not_included(
            self, folders_fixture, files_fixture):
        self.config.include = 'image1'
        self.user.getPhotosets.return_value = folders_fixture
        folders_fixture[0].getPhotos.return_value = files_fixture
        storage = FlickrStorage(self.config, Resiliently(self.config))
        folders = list(storage.list_folders())
        files = list(storage.list_files(folders[0]))

        assert len(files) == 1
        assert files[0].name == 'image1.jpg'
    def test_should_throttle_consecutive_calls(self):
        time_patch = patch('album_rsync.throttle.time.time', create=True)
        mock_time = time_patch.start()
        self.config.throttling = 10
        resiliently = Resiliently(self.config)

        mock_time.return_value = 0
        resiliently.call(self.callback, 'a', b='b')
        mock_time.return_value = 1
        resiliently.call(self.callback, 'a', b='b')
        mock_time.return_value = 6
        resiliently.call(self.callback, 'a', b='b')

        self.mock_sleep.assert_has_calls_exactly([call(9), call(5)])
        time_patch.stop()
    def test_should_throttle_consecutive_calls_across_multiple_functions(self):
        time_patch = patch('album_rsync.throttle.time.time', create=True)
        mock_time = time_patch.start()
        self.config.throttling = 10
        callback2 = MagicMock()
        callback2.__name__ = 'bar'
        resiliently = Resiliently(self.config)

        mock_time.return_value = 0
        resiliently.call(self.callback, 'a', b='b')
        mock_time.return_value = 1
        resiliently.call(callback2, 'a', b='b')
        mock_time.return_value = 6
        resiliently.call(self.callback, 'a', b='b')

        self.mock_sleep.assert_has_calls_exactly([call(9), call(5)])
        time_patch.stop()
    def test_should_not_throttle_if_timeout_passed(self):
        time_patch = patch('album_rsync.throttle.time.time', create=True)
        mock_time = time_patch.start()
        self.config.throttling = 10
        resiliently = Resiliently(self.config)

        mock_time.return_value = 0
        resiliently.call(self.callback, 'a', b='b')
        mock_time.return_value = 11
        resiliently.call(self.callback, 'a', b='b')

        self.mock_sleep.assert_not_called()
        time_patch.stop()
    def test_should_make_remote_call(self):
        resiliently = Resiliently(self.config)
        resiliently.call(self.callback, 'a', b='b')

        self.callback.assert_called_once_with('a', b='b')