Source code for test.cata_log_hub.test_opds

# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Cata-Log - the central hub for grocery store catalogs
# Copyright (C) 2026 David Aderbauer & The Cata-Log Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

import xml.etree.ElementTree as ET
from io import BytesIO

from ebooklib import epub
from fastapi import status


[docs] def test_get_opds_catalog_overview(client, full_database): response = client.get("/opds/") assert response.status_code == status.HTTP_200_OK assert response.headers assert "Content-Type" in response.headers assert response.headers["Content-Type"] == "application/atom+xml" content = response.content assert content xml_root = ET.fromstring(content) assert xml_root.tag == "{http://www.w3.org/2005/Atom}feed" entries = xml_root.findall("atom:entry", {"atom": "http://www.w3.org/2005/Atom"}) assert entries links = xml_root.findall("atom:link", {"atom": "http://www.w3.org/2005/Atom"}) assert links
[docs] def test_get_opds_catalog_overview__noauth(noauth_client, full_database): response = noauth_client.get("/opds/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_overview__bad_auth(bad_auth_client, full_database): response = bad_auth_client.get("/opds/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_latest(client, full_database): response = client.get("/opds/latest/") assert response.status_code == status.HTTP_200_OK assert response.headers assert "Content-Type" in response.headers assert response.headers["Content-Type"] == "application/atom+xml" content = response.content assert content xml_root = ET.fromstring(content) assert xml_root.tag == "{http://www.w3.org/2005/Atom}feed" entries = xml_root.findall("atom:entry", {"atom": "http://www.w3.org/2005/Atom"}) assert entries links = xml_root.findall("atom:link", {"atom": "http://www.w3.org/2005/Atom"}) assert links
[docs] def test_get_opds_catalog_latest__noauth(noauth_client, full_database): response = noauth_client.get("/opds/latest/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_latest__bad_auth(bad_auth_client, full_database): response = bad_auth_client.get("/opds/latest/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_all(client, full_database): response = client.get("/opds/all/") assert response.status_code == status.HTTP_200_OK assert response.headers assert "Content-Type" in response.headers assert response.headers["Content-Type"] == "application/atom+xml" content = response.content assert content xml_root = ET.fromstring(content) assert xml_root.tag == "{http://www.w3.org/2005/Atom}feed" entries = xml_root.findall("atom:entry", {"atom": "http://www.w3.org/2005/Atom"}) assert entries links = xml_root.findall("atom:link", {"atom": "http://www.w3.org/2005/Atom"}) assert links
[docs] def test_get_opds_catalog_all__noauth(noauth_client, full_database): response = noauth_client.get("/opds/all/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_all__bad_auth(bad_auth_client, full_database): response = bad_auth_client.get("/opds/all/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_current(client, full_database): response = client.get("/opds/current/") assert response.status_code == status.HTTP_200_OK assert response.headers assert "Content-Type" in response.headers assert response.headers["Content-Type"] == "application/atom+xml" content = response.content assert content xml_root = ET.fromstring(content) assert xml_root.tag == "{http://www.w3.org/2005/Atom}feed" entries = xml_root.findall("atom:entry", {"atom": "http://www.w3.org/2005/Atom"}) assert entries links = xml_root.findall("atom:link", {"atom": "http://www.w3.org/2005/Atom"}) assert links
[docs] def test_get_opds_catalog_current__noauth(noauth_client, full_database): response = noauth_client.get("/opds/current/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_current__bad_auth(bad_auth_client, full_database): response = bad_auth_client.get("/opds/current/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_previews(client, full_database): response = client.get("/opds/previews/") assert response.status_code == status.HTTP_200_OK assert response.headers assert "Content-Type" in response.headers assert response.headers["Content-Type"] == "application/atom+xml" content = response.content assert content xml_root = ET.fromstring(content) assert xml_root.tag == "{http://www.w3.org/2005/Atom}feed" entries = xml_root.findall("atom:entry", {"atom": "http://www.w3.org/2005/Atom"}) assert entries links = xml_root.findall("atom:link", {"atom": "http://www.w3.org/2005/Atom"}) assert links
[docs] def test_get_opds_catalog_previews__noauth(noauth_client, full_database): response = noauth_client.get("/opds/previews/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_previews__bad_auth(bad_auth_client, full_database): response = bad_auth_client.get("/opds/previews/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_outdated(client, full_database): response = client.get("/opds/outdated/") assert response.status_code == status.HTTP_200_OK assert response.headers assert "Content-Type" in response.headers assert response.headers["Content-Type"] == "application/atom+xml" content = response.content assert content xml_root = ET.fromstring(content) assert xml_root.tag == "{http://www.w3.org/2005/Atom}feed" entries = xml_root.findall("atom:entry", {"atom": "http://www.w3.org/2005/Atom"}) assert entries links = xml_root.findall("atom:link", {"atom": "http://www.w3.org/2005/Atom"}) assert links
[docs] def test_get_opds_catalog_outdated__noauth(noauth_client, full_database): response = noauth_client.get("/opds/outdated/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_outdated__bad_auth(bad_auth_client, full_database): response = bad_auth_client.get("/opds/outdated/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_providers(client, full_database): response = client.get("/opds/providers/") assert response.status_code == status.HTTP_200_OK assert response.headers assert "Content-Type" in response.headers assert response.headers["Content-Type"] == "application/atom+xml" content = response.content assert content xml_root = ET.fromstring(content) assert xml_root.tag == "{http://www.w3.org/2005/Atom}feed" entries = xml_root.findall("atom:entry", {"atom": "http://www.w3.org/2005/Atom"}) assert entries links = xml_root.findall("atom:link", {"atom": "http://www.w3.org/2005/Atom"}) assert links
[docs] def test_get_opds_catalog_providers__noauth(noauth_client, full_database): response = noauth_client.get("/opds/providers/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_providers__bad_auth(bad_auth_client, full_database): response = bad_auth_client.get("/opds/providers/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_provider(client, fake_provider, full_database): response = client.get(f"/opds/providers/{fake_provider.id}/") assert response.status_code == status.HTTP_200_OK assert response.headers assert "Content-Type" in response.headers assert response.headers["Content-Type"] == "application/atom+xml" content = response.content assert content xml_root = ET.fromstring(content) assert xml_root.tag == "{http://www.w3.org/2005/Atom}feed" entries = xml_root.findall("atom:entry", {"atom": "http://www.w3.org/2005/Atom"}) assert entries links = xml_root.findall("atom:link", {"atom": "http://www.w3.org/2005/Atom"}) assert links
[docs] def test_get_opds_catalog_provider__noauth(noauth_client, fake_provider, full_database): response = noauth_client.get(f"/opds/providers/{fake_provider.id}/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_provider__bad_auth( bad_auth_client, fake_provider, full_database ): response = bad_auth_client.get(f"/opds/providers/{fake_provider.id}/") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_catalog_epub(client, fake_catalog, fake_page, full_database): response = client.get(f"/opds/{fake_catalog.id}.epub") assert response.status_code == status.HTTP_200_OK assert response.headers assert "Content-Type" in response.headers assert response.headers["Content-Type"] == "application/epub+zip" content = response.content assert content book = epub.read_epub(BytesIO(content)) assert book.get_metadata("DC", "title") assert book.get_metadata("DC", "identifier") assert book.get_metadata("DC", "creator") assert book.get_metadata("DC", "language") assert book.toc assert book.spine page_image = book.get_item_with_id(str(fake_page.id)) assert page_image assert page_image.file_name == fake_page.file.name assert page_image.content == fake_page.file.path.read_bytes() page_chapter = book.get_item_with_href(f"chap_{fake_page.number + 1}.xhtml") assert page_chapter assert "img" in page_chapter.content.decode() assert f'src="{fake_page.file.name}"' in page_chapter.content.decode()
[docs] def test_get_catalog_epub__noauth(noauth_client, fake_catalog, full_database): response = noauth_client.get(f"/opds/{fake_catalog.id}.epub") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog___bad_auth(bad_auth_client, fake_catalog, full_database): response = bad_auth_client.get(f"/opds/{fake_catalog.id}.epub") assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.headers assert "WWW-Authenticate" in response.headers assert response.headers["WWW-Authenticate"] == "Basic"
[docs] def test_get_opds_catalog_provider__not_found(client, full_database): response = client.get("/opds/215.epub") assert response.status_code == status.HTTP_404_NOT_FOUND assert response.json()["detail"] == "Catalog not found"