From 9ab1c8afcb97954115d96abdfee01122d8478d61 Mon Sep 17 00:00:00 2001 From: andy Date: Sun, 7 Feb 2021 15:03:26 +0000 Subject: [PATCH] added counter tests, set up CI --- .github/workflows/ci.yml | 34 ++++ .vscode/settings.json | 12 ++ pyproject.toml | 11 +- scripts.py | 17 ++ tests/test_counter.py | 421 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 492 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .vscode/settings.json create mode 100644 scripts.py create mode 100644 tests/test_counter.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7b0ea09 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: Tests +on: [push, pull_request] + +jobs: + build: + strategy: + fail-fast: false + matrix: + python-version: [3.8] + poetry-version: [1.1.4] + os: [ubuntu-20.04, ubuntu-18.04, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 # get source + + - name: Install Python 3 + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Poetry # dependency management + uses: abatilo/actions-poetry@v2.1.0 + with: + poetry-version: ${{ matrix.poetry-version }} + + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + poetry run flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + poetry run flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + - name: Run tests with pytest # test script + run: poetry run test \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..58fed07 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "python.testing.unittestArgs": [ + "-v", + "-s", + "./tests", + "-p", + "test*.py" + ], + "python.testing.pytestEnabled": false, + "python.testing.nosetestsEnabled": false, + "python.testing.unittestEnabled": true +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index d344b57..4c835da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,11 +8,16 @@ repository = "https://github.com/Sarsoo/spotfm" [tool.poetry.dependencies] python = "^3.8" -spotframework = { path = "../spotframework" } -fmframework = { path = "../fmframework" } +spotframework = { git = "https://github.com/Sarsoo/spotframework.git" } +fmframework = { git = "https://github.com/Sarsoo/pyfmframework.git" } [tool.poetry.dev-dependencies] -pylint = "2.5.3" +pylint = "^2.5.3" +flake8 = "^3.8.4" + +[tool.poetry.scripts] +test = 'scripts:test' +testv = 'scripts:testv' [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/scripts.py b/scripts.py new file mode 100644 index 0000000..49ba7a4 --- /dev/null +++ b/scripts.py @@ -0,0 +1,17 @@ +import subprocess + +def test(): + """ + Run all unittests. + """ + subprocess.run( + ['python', '-u', '-m', 'unittest', 'discover', "-s", "tests"] + ) + +def testv(): + """ + Run all unittests with verbose. + """ + subprocess.run( + ['python', '-u', '-m', 'unittest', 'discover', "-v", "-s", "tests"] + ) \ No newline at end of file diff --git a/tests/test_counter.py b/tests/test_counter.py new file mode 100644 index 0000000..69dac15 --- /dev/null +++ b/tests/test_counter.py @@ -0,0 +1,421 @@ +import unittest +from unittest.mock import Mock, MagicMock, create_autospec, patch + +from dataclasses import fields + +from spotfm.maths.counter import Counter +from spotframework.model.uri import Uri +from spotframework.net.network import SpotifyNetworkException +from fmframework.net.network import LastFMNetworkException + +class TestCounter(unittest.TestCase): + + ### ARTIST ### + + def test_artist_no_input(self): + spotnet = Mock() + fmnet = Mock() + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + with self.assertRaises(ValueError): + counter.count_artist() + + def test_artist_with_artist_obj(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.artist.return_value = return_mock + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + artist_mock = Mock() + artist_mock.name = 'artist' + + answer = counter.count_artist(artist=artist_mock) + + fmnet.artist.assert_called_once() + self.assertEqual(answer, 10) + + def test_artist_with_artist_obj_no_response(self): + spotnet = Mock() + fmnet = Mock() + + fmnet.artist.return_value = None + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + artist_mock = Mock() + artist_mock.name = 'artist' + + answer = counter.count_artist(artist=artist_mock) + + fmnet.artist.assert_called_once() + self.assertEqual(answer, 0) + + def test_artist_network_error(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.artist.side_effect = LastFMNetworkException(500, 5) + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + artist_mock = Mock() + artist_mock.name = 'artist' + + answer = counter.count_artist(artist=artist_mock) + + fmnet.artist.assert_called_once() + self.assertEqual(answer, 0) + + def test_artist_with_uri(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.artist.return_value = return_mock + + spot_return_mock = Mock() + spot_return_mock.name = 'artist' + spotnet.artist.return_value = spot_return_mock + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + uri_mock = Mock() + uri_mock.object_type = Uri.ObjectType.artist + + answer = counter.count_artist(uri=uri_mock) + + fmnet.artist.assert_called_once() + self.assertEqual(answer, 10) + + def test_artist_with_uri_wrong_type(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.artist.return_value = return_mock + + spot_return_mock = Mock() + spot_return_mock.name = 'artist' + spotnet.artist.return_value = spot_return_mock + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + uri_mock = Mock() + uri_mock.object_type = Uri.ObjectType.track + + with self.assertRaises(ValueError): + answer = counter.count_artist(uri=uri_mock) + + fmnet.artist.assert_called_once() + self.assertEqual(answer, 10) + + def test_artist_with_uri_network_error(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.artist.return_value = return_mock + + spot_return_mock = Mock() + spot_return_mock.name = 'artist' + spotnet.artist.return_value = spot_return_mock + spotnet.artist.side_effect = SpotifyNetworkException(500) + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + uri_mock = Mock() + uri_mock.object_type = Uri.ObjectType.artist + + answer = counter.count_artist(uri=uri_mock) + + spotnet.artist.assert_called_once() + fmnet.artist.assert_not_called() + self.assertEqual(answer, 0) + + ### ALBUM ### + + def test_album_no_input(self): + spotnet = Mock() + fmnet = Mock() + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + with self.assertRaises(ValueError): + counter.count_album() + + def test_album_with_artist_obj(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.album.return_value = return_mock + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + album_mock = Mock() + album_mock.name = 'album' + album_mock.artists = [Mock()] + + answer = counter.count_album(album=album_mock) + + fmnet.album.assert_called_once() + self.assertEqual(answer, 10) + + def test_album_with_album_obj_no_response(self): + spotnet = Mock() + fmnet = Mock() + + fmnet.album.return_value = None + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + album_mock = Mock() + album_mock.name = 'album' + album_mock.artists = [Mock()] + + answer = counter.count_album(album=album_mock) + + fmnet.album.assert_called_once() + self.assertEqual(answer, 0) + + def test_album_network_error(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.album.side_effect = LastFMNetworkException(500, 5) + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + album_mock = Mock() + album_mock.name = 'album' + album_mock.artists = [Mock()] + + answer = counter.count_album(album=album_mock) + + fmnet.album.assert_called_once() + self.assertEqual(answer, 0) + + def test_album_with_uri(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.album.return_value = return_mock + + spot_return_mock = Mock() + spot_return_mock.name = 'album' + spot_return_mock.artists = [Mock()] + spotnet.album.return_value = spot_return_mock + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + uri_mock = Mock() + uri_mock.object_type = Uri.ObjectType.album + + answer = counter.count_album(uri=uri_mock) + + fmnet.album.assert_called_once() + self.assertEqual(answer, 10) + + def test_album_with_uri_wrong_type(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.album.return_value = return_mock + + spot_return_mock = Mock() + spot_return_mock.name = 'album' + spotnet.album.return_value = spot_return_mock + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + uri_mock = Mock() + uri_mock.object_type = Uri.ObjectType.track + + with self.assertRaises(ValueError): + answer = counter.count_album(uri=uri_mock) + + fmnet.album.assert_called_once() + self.assertEqual(answer, 10) + + def test_album_with_uri_network_error(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.album.return_value = return_mock + + spot_return_mock = Mock() + spot_return_mock.name = 'album' + spotnet.album.return_value = spot_return_mock + spotnet.album.side_effect = SpotifyNetworkException(500) + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + uri_mock = Mock() + uri_mock.object_type = Uri.ObjectType.album + + answer = counter.count_album(uri=uri_mock) + + spotnet.album.assert_called_once() + fmnet.album.assert_not_called() + self.assertEqual(answer, 0) + + ### TRACK ### + + def test_track_no_input(self): + spotnet = Mock() + fmnet = Mock() + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + with self.assertRaises(ValueError): + counter.count_track() + + def test_track_with_artist_obj(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.track.return_value = return_mock + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + track_mock = Mock() + track_mock.name = 'track' + track_mock.artists = [Mock()] + + answer = counter.count_track(track=track_mock) + + fmnet.track.assert_called_once() + self.assertEqual(answer, 10) + + def test_track_with_album_obj_no_response(self): + spotnet = Mock() + fmnet = Mock() + + fmnet.track.return_value = None + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + track_mock = Mock() + track_mock.name = 'track' + track_mock.artists = [Mock()] + + answer = counter.count_track(track=track_mock) + + fmnet.track.assert_called_once() + self.assertEqual(answer, 0) + + def test_track_network_error(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.track.side_effect = LastFMNetworkException(500, 5) + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + track_mock = Mock() + track_mock.name = 'track' + track_mock.artists = [Mock()] + + answer = counter.count_track(track=track_mock) + + fmnet.track.assert_called_once() + self.assertEqual(answer, 0) + + def test_track_with_uri(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.track.return_value = return_mock + + spot_return_mock = Mock() + spot_return_mock.name = 'track' + spot_return_mock.artists = [Mock()] + spotnet.track.return_value = spot_return_mock + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + uri_mock = Mock() + uri_mock.object_type = Uri.ObjectType.track + + answer = counter.count_track(uri=uri_mock) + + fmnet.track.assert_called_once() + self.assertEqual(answer, 10) + + def test_track_with_uri_wrong_type(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.track.return_value = return_mock + + spot_return_mock = Mock() + spot_return_mock.name = 'track' + spotnet.track.return_value = spot_return_mock + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + uri_mock = Mock() + uri_mock.object_type = Uri.ObjectType.album + + with self.assertRaises(ValueError): + answer = counter.count_track(uri=uri_mock) + + fmnet.track.assert_called_once() + self.assertEqual(answer, 10) + + def test_track_with_uri_network_error(self): + spotnet = Mock() + fmnet = Mock() + + return_mock = Mock() + return_mock.user_scrobbles = 10 + fmnet.track.return_value = return_mock + + spot_return_mock = Mock() + spot_return_mock.name = 'track' + spotnet.track.return_value = spot_return_mock + spotnet.track.side_effect = SpotifyNetworkException(500) + + counter = Counter(spotnet=spotnet, fmnet=fmnet) + + uri_mock = Mock() + uri_mock.object_type = Uri.ObjectType.track + + answer = counter.count_track(uri=uri_mock) + + spotnet.track.assert_called_once() + fmnet.track.assert_not_called() + self.assertEqual(answer, 0) + + #TODO: count_playlist method + #TODO: count method + +if __name__ == '__main__': + unittest.main() \ No newline at end of file