diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5f04645 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: CI +on: + push: + branches: master + pull_request: +jobs: + test: + runs-on: ubuntu-latest + name: Test (Python ${{ matrix.python-version }}) + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "${{ matrix.python-version }}" + - name: Install tox + run: | + python -m pip install --upgrade pip + pip install tox tox-gh-actions + - name: Run tests + run: tox + diff --git a/.gitignore b/.gitignore index 3a78423..d6164aa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ __pycache__ dist build jellyfin_apiclient_python.egg-info +.tox diff --git a/README.md b/README.md index a48b148..29848a3 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,14 @@ client.jellyfin.search_media_items( For details on what the individual API calls do or how to do a certain task, you will probably find the [Jellyfin MPV Shim](https://github.com/iwalton3/jellyfin-mpv-shim) and [Jellyfin Kodi](https://github.com/jellyfin/jellyfin-kodi) repositories useful. +## Running the tests + +The test suite is run via `tox`, and you can install it from PyPi. + + - To run the linter: `tox -elint` + - To run the test suite, for Python 3.9: `tox -epy39` + - You can run multiple environments, if you wish: `tox -elint,py311` + ## Changes from Jellyfin Kodi - Removal of `websocket.py` (now a dependency to `websocket_client`). diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_credentials.py b/tests/test_credentials.py new file mode 100644 index 0000000..af23851 --- /dev/null +++ b/tests/test_credentials.py @@ -0,0 +1,84 @@ +import unittest + +from jellyfin_apiclient_python.credentials import Credentials + + +class TestCredientials(unittest.TestCase): + def setUp(self): + self.credentials = Credentials() + + def assertEmptyCredentials(self, credentials): + empty_creds = {"Servers": []} + self.assertEqual(credentials, empty_creds) + + def test_set_credentials_and_get_credentials(self): + credentials = {"Server": "Foo"} + self.credentials.set_credentials(credentials) + self.assertEqual(self.credentials.get_credentials(), credentials) + + def test_get_credentials(self): + empty = self.credentials.get_credentials() + self.assertEmptyCredentials(empty) + + def test_set_credentials_evals_to_false(self): + self.credentials.set_credentials(False) + # get() then swallows the ValueError, and returns empty credentials, + # but only if self.credentials evaluates to False + self.assertEmptyCredentials(self.credentials.get()) + + def test_get(self): + empty = self.credentials.get() + self.assertEmptyCredentials(empty) + + def test_set(self): + data = {"User": "foobar"} + self.credentials.set(data) + self.assertEqual(self.credentials.get(), data) + # Passing anything that evaluates to false will clear it + self.credentials.set(False) + self.assertEmptyCredentials(self.credentials.get()) + + def test_add_update_user(self): + server = {} + user_data = {"Id": 42} + self.credentials.add_update_user(server, user_data) + self.assertEqual(server, {"Users": [user_data]}) + # If we re-add the same user, it is unchanged. + self.credentials.add_update_user(server, user_data) + self.assertEqual(server, {"Users": [user_data]}) + + def test_add_update_server_no_id(self): + with self.assertRaises(KeyError): + self.credentials.add_update_server({}, {"Foo": "Bar"}) + + def test_add_update_server_adds_datelastaccessed(self): + servers = [] + server = {"Id": 42} + new_server = self.credentials.add_update_server(servers, server) + server["DateLastAccessed"] = "2001-01-01T00:00:00Z" + self.assertEqual(new_server, server) + + def test_add_update_server_merges_data(self): + # DateLastAccessed must be older for merges to happen + servers = [{"Id": 42, "DateLastAccessed": "1999-12-31T23:58:59Z"}] + server = { + "Id": 42, "AccessToken": "xx", "UserId": 1, "UserLinkType": "Foo", + "ExchangeToken": "Bar", "ManualAddress": 2, "LocalAddress": 3, + "Name": "Test", "LastConnectionMode": "Unknown", + "ConnectServerId": 44 + } + new_server = self.credentials.add_update_server(servers, server) + self.assertEqual(new_server, server) + + def test_add_update_server_does_not_merge_if_newer(self): + servers = [{"Id": 42, "DateLastAccessed": "2023-10-01T01:02:03Z"}] + server = {"Id": 42} + new_server = self.credentials.add_update_server(servers, server) + self.assertEqual(new_server, servers[0]) + + def test_add_update_server_does_not_merge_unknown_fields(self): + servers = [{"Id": 42, "DateLastAccessed": "1999-12-31T23:58:59Z"}] + server = {"Id": 42, "Foo": "Bar"} + new_server = self.credentials.add_update_server(servers, server) + merged_server = {"Id": 42, "DateLastAccessed": "2001-01-01T00:00:00Z"} + self.assertEqual(new_server, merged_server) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..66f9b3a --- /dev/null +++ b/tox.ini @@ -0,0 +1,25 @@ +[tox] +envlist = + lint, + py{38,39,310,311,312}, + +[gh-actions] +python = + 3.8: py38 + 3.9: py39 + 3.10: py310, lint + 3.11: py311 + 3.12: py312 + +[testenv] +deps = + pytest +commands = pytest {posargs} + +[testenv:lint] +basepython = python3.10 +skip_install = true +deps = + ruff +commands = + ruff check --exit-non-zero-on-fix jellyfin_apiclient_python tests