bump version

This commit is contained in:
hyugogirubato
2026-04-06 15:01:01 +02:00
parent 0ecf0f843b
commit 95472d4ac4
20 changed files with 2198 additions and 1461 deletions
+23 -25
View File
@@ -1,55 +1,53 @@
"""Test fixtures for the CBZ library."""
from pathlib import Path
import pytest
from cbz.comic import ComicInfo
from cbz.page import PageInfo
from cbz.constants import PageType
from cbz.page import PageInfo
@pytest.fixture
def fixtures_dir() -> Path:
"""Fixture that provides the path to the test fixtures directory."""
return Path(__file__).parent / 'fixtures'
"""Path to the test fixtures directory."""
return Path(__file__).parent / "fixtures"
@pytest.fixture
def images_dir(fixtures_dir: Path) -> Path:
"""Fixture that provides the path to the test images directory."""
return fixtures_dir / 'images'
"""Path to the test images directory."""
return fixtures_dir / "images"
@pytest.fixture
def sample_image_path(images_dir: Path) -> Path:
"""Fixture that provides a sample image path."""
return images_dir / 'page-000.jpg'
"""Path to a sample test image."""
return images_dir / "page-000.jpg"
@pytest.fixture
def sample_cbz_file(tmp_path: Path, images_dir: Path) -> Path:
"""Fixture that creates a sample CBZ file for testing."""
# Load sample pages
image_paths = sorted(list(images_dir.iterdir()))[:3] # Use first 3 images
pages = []
"""Create a temporary CBZ file for testing."""
image_paths = sorted(list(images_dir.iterdir()))[:3]
pages = [
PageInfo.load(
path=path,
type=PageType.FRONT_COVER if i == 0 else PageType.STORY,
)
for i, path in enumerate(image_paths)
]
for i, path in enumerate(image_paths):
page_type = PageType.FRONT_COVER if i == 0 else PageType.STORY
page = PageInfo.load(path=path, type=page_type)
pages.append(page)
# Create comic from pages
comic = ComicInfo.from_pages(
pages=pages,
title='Test Comic',
series='Test Series',
title="Test Comic",
series="Test Series",
number=1,
volume=1,
year=2024
year=2024,
)
# Save to temporary file
cbz_path = tmp_path / 'test_comic.cbz'
cbz_content = comic.pack()
cbz_path.write_bytes(cbz_content)
cbz_path = tmp_path / "test_comic.cbz"
cbz_path.write_bytes(comic.pack())
return cbz_path
+36 -31
View File
@@ -1,59 +1,64 @@
"""Usage example for the CBZ library."""
from pathlib import Path
from cbz.comic import ComicInfo
from cbz.constants import PageType, YesNo, Manga, AgeRating, Format
from cbz.page import PageInfo
from cbz import ComicInfo, PageInfo, PageType, Format, YesNo, Manga, AgeRating
PARENT = Path(__file__).parent
if __name__ == '__main__':
paths = list((PARENT / 'fixtures' / 'images').iterdir())
if __name__ == "__main__":
paths = sorted((PARENT / "fixtures" / "images").iterdir())
# Load each page from the 'images' folder into a list of PageInfo objects
# Load pages with automatic type assignment
pages = [
PageInfo.load(
path=path,
type=PageType.FRONT_COVER if i == 0 else PageType.BACK_COVER if i == len(paths) - 1 else PageType.STORY
type=(
PageType.FRONT_COVER if i == 0
else PageType.BACK_COVER if i == len(paths) - 1
else PageType.STORY
),
)
for i, path in enumerate(paths)
]
# Create a ComicInfo object using ComicInfo.from_pages() method
# Create comic with metadata
comic = ComicInfo.from_pages(
pages=pages,
title='T1 - Arrête de me chauffer, Nagatoro',
series='Arrête de me chauffer, Nagatoro',
title="T1 - Arrête de me chauffer, Nagatoro",
series="Arrête de me chauffer, Nagatoro",
number=1,
count=8,
volume=1,
summary='Nagatoro est en seconde. Pleine d\u2019assurance, joueuse, moqueuse, elle se d\u00e9couvre un jour un passe-temps favori : martyriser son \u201cSenpai\u201d, lyc\u00e9en de premi\u00e8re timide et mal dans sa peau. Nagatoro taquine, agace, aguiche, va parfois trop loin... mais qu\u2019a-t-elle vraiment derri\u00e8re la t\u00eate ? Et si derri\u00e8re ses moqueries elle cachait une v\u00e9ritable affection ? Et si finalement, ses farces permettaient \u00e0 Senpai de s\u2019affirmer ?',
summary="Nagatoro est en seconde. Pleine d\u2019assurance, joueuse, moqueuse, elle se d\u00e9couvre un jour un passe-temps favori : martyriser son \u201cSenpai\u201d, lyc\u00e9en de premi\u00e8re timide et mal dans sa peau. Nagatoro taquine, agace, aguiche, va parfois trop loin... mais qu\u2019a-t-elle vraiment derri\u00e8re la t\u00eate ? Et si derri\u00e8re ses moqueries elle cachait une v\u00e9ritable affection ? Et si finalement, ses farces permettaient \u00e0 Senpai de s\u2019affirmer ?",
year=2021,
month=3,
day=12,
writer='Nanashi',
inker='Nanashi',
editor='Noeve Grafx',
publisher='Noeve Grafx',
imprint='Noeve Grafx',
genre='Shonen',
web='http://www.izneo.com/en/manga/shonen/arrete-de-me-chauffer-nagatoro-37560/arrete-de-me-chauffer-nagatoro-86232',
language_iso='fr',
writer="Nanashi",
inker="Nanashi",
editor="Noeve Grafx",
publisher="Noeve Grafx",
imprint="Noeve Grafx",
genre="Shonen",
web="http://www.izneo.com/en/manga/shonen/arrete-de-me-chauffer-nagatoro-37560/arrete-de-me-chauffer-nagatoro-86232",
language_iso="fr",
format=Format.PREVIEW,
black_white=YesNo.YES,
manga=Manga.RIGHT_LEFT,
age_rating=AgeRating.EVERYONE10,
manga=Manga.YES_AND_RIGHT_TO_LEFT,
age_rating=AgeRating.EVERYONE_10_PLUS,
community_rating=5,
ean='9782490676569'
ean="9782490676569",
)
# Show the comic using the show()
# Use the sequence protocol
print(f"Number of pages: {len(comic)}")
for i, page in enumerate(comic):
print(f" Page {i}: {page.image_width}x{page.image_height} ({page.type})")
# Display in the built-in reader
comic.show()
# Pack the comic book content into a CBZ file format
cbz_content = comic.pack(rename=True)
# Define the path where the CBZ file will be saved
cbz_path = PARENT / f'{comic.title}.cbz'
# Write the CBZ content to the specified path
cbz_path.write_bytes(cbz_content)
# Save as CBZ
cbz_path = PARENT / f"{comic.title}.cbz"
comic.save(cbz_path)
print(f"Saved: {cbz_path}")
+130 -88
View File
@@ -1,166 +1,155 @@
"""Tests for the ComicInfo class."""
import tempfile
from pathlib import Path
from typing import List
from cbz.comic import ComicInfo
from cbz.constants import AgeRating, Format, Manga, PageType, YesNo
from cbz.page import PageInfo
from cbz.constants import PageType, YesNo, Manga, AgeRating, Format
class TestComicInfo:
"""Test cases for ComicInfo class."""
"""Tests for comic creation, loading and serialization."""
def test_from_pages_creation(self, images_dir: Path) -> None:
"""Test creating ComicInfo from pages."""
# Load sample pages
image_paths = sorted(list(images_dir.iterdir()))[:3] # Use first 3 images
pages: List[PageInfo] = []
"""Create a ComicInfo from pages."""
image_paths = sorted(list(images_dir.iterdir()))[:3]
pages = [
PageInfo.load(
path=path,
type=PageType.FRONT_COVER if i == 0 else PageType.STORY,
)
for i, path in enumerate(image_paths)
]
for i, path in enumerate(image_paths):
page_type = PageType.FRONT_COVER if i == 0 else PageType.STORY
page = PageInfo.load(path=path, type=page_type)
pages.append(page)
# Create comic from pages
comic = ComicInfo.from_pages(
pages=pages,
title='Test Comic',
series='Test Series',
title="Test Comic",
series="Test Series",
number=1,
volume=1,
year=2024
year=2024,
)
assert comic.title == 'Test Comic'
assert comic.series == 'Test Series'
assert comic.title == "Test Comic"
assert comic.series == "Test Series"
assert comic.number == 1
assert comic.volume == 1
assert comic.year == 2024
assert len(comic.pages) == 3
assert comic.pages[0].type == PageType.FRONT_COVER
assert comic.pages[1].type == PageType.STORY
assert len(comic) == 3
assert comic[0].type == PageType.FRONT_COVER
assert comic[1].type == PageType.STORY
def test_from_cbz_file(self, sample_cbz_file: Path) -> None:
"""Test loading ComicInfo from CBZ file."""
"""Load from a CBZ file."""
comic = ComicInfo.from_cbz(sample_cbz_file)
assert comic is not None
assert hasattr(comic, 'pages')
assert len(comic.pages) > 0
assert all(isinstance(page, PageInfo) for page in comic.pages)
assert len(comic) > 0
assert all(isinstance(page, PageInfo) for page in comic)
def test_pack_cbz(self, images_dir: Path) -> None:
"""Test packing comic into CBZ format."""
# Create a simple comic
"""Pack into CBZ format."""
image_paths = sorted(list(images_dir.iterdir()))[:2]
pages = [PageInfo.load(path=path) for path in image_paths]
comic = ComicInfo.from_pages(
pages=pages,
title='Pack Test',
series='Test Series'
title="Pack Test",
series="Test Series",
)
# Pack to CBZ
cbz_content = comic.pack()
assert isinstance(cbz_content, bytes)
assert len(cbz_content) > 0
def test_pack_with_rename(self, images_dir: Path) -> None:
"""Test packing comic with page renaming."""
"""Pack with sequential page renaming."""
image_paths = sorted(list(images_dir.iterdir()))[:2]
pages = [PageInfo.load(path=path) for path in image_paths]
comic = ComicInfo.from_pages(
pages=pages,
title='Rename Test'
)
# Pack with rename option
comic = ComicInfo.from_pages(pages=pages, title="Rename Test")
cbz_content = comic.pack(rename=True)
assert isinstance(cbz_content, bytes)
assert len(cbz_content) > 0
def test_comic_metadata_properties(self, images_dir: Path) -> None:
"""Test comic metadata properties."""
"""Verify all metadata fields."""
image_paths = sorted(list(images_dir.iterdir()))[:1]
pages = [PageInfo.load(path=path) for path in image_paths]
comic = ComicInfo.from_pages(
pages=pages,
title='Metadata Test',
series='Test Series',
title="Metadata Test",
series="Test Series",
number=5,
count=10,
volume=2,
summary='Test summary',
summary="Test summary",
year=2023,
month=6,
day=15,
writer='Test Writer',
penciller='Test Penciller',
inker='Test Inker',
colorist='Test Colorist',
letterer='Test Letterer',
cover_artist='Test Cover Artist',
editor='Test Editor',
publisher='Test Publisher',
imprint='Test Imprint',
genre='Test Genre',
language_iso='en',
writer="Test Writer",
penciller="Test Penciller",
inker="Test Inker",
colorist="Test Colorist",
letterer="Test Letterer",
cover_artist="Test Cover Artist",
editor="Test Editor",
publisher="Test Publisher",
imprint="Test Imprint",
genre="Test Genre",
language_iso="en",
format=Format.SERIES,
black_white=YesNo.NO,
manga=Manga.RIGHT_LEFT,
manga=Manga.YES_AND_RIGHT_TO_LEFT,
age_rating=AgeRating.TEEN,
community_rating=4
community_rating=4,
)
assert comic.title == 'Metadata Test'
assert comic.series == 'Test Series'
assert comic.title == "Metadata Test"
assert comic.series == "Test Series"
assert comic.number == 5
assert comic.count == 10
assert comic.volume == 2
assert comic.summary == 'Test summary'
assert comic.summary == "Test summary"
assert comic.year == 2023
assert comic.month == 6
assert comic.day == 15
assert comic.writer == 'Test Writer'
assert comic.penciller == 'Test Penciller'
assert comic.inker == 'Test Inker'
assert comic.colorist == 'Test Colorist'
assert comic.letterer == 'Test Letterer'
assert comic.cover_artist == 'Test Cover Artist'
assert comic.editor == 'Test Editor'
assert comic.publisher == 'Test Publisher'
assert comic.imprint == 'Test Imprint'
assert comic.genre == 'Test Genre'
assert comic.language_iso == 'en'
assert comic.writer == "Test Writer"
assert comic.penciller == "Test Penciller"
assert comic.inker == "Test Inker"
assert comic.colorist == "Test Colorist"
assert comic.letterer == "Test Letterer"
assert comic.cover_artist == "Test Cover Artist"
assert comic.editor == "Test Editor"
assert comic.publisher == "Test Publisher"
assert comic.imprint == "Test Imprint"
assert comic.genre == "Test Genre"
assert comic.language_iso == "en"
assert comic.format == Format.SERIES
assert comic.black_white == YesNo.NO
assert comic.manga == Manga.RIGHT_LEFT
assert comic.manga == Manga.YES_AND_RIGHT_TO_LEFT
assert comic.age_rating == AgeRating.TEEN
assert comic.community_rating == 4
def test_page_count_property(self, images_dir: Path) -> None:
"""Test that page count returns correct count."""
"""Verify page count via len()."""
image_paths = sorted(list(images_dir.iterdir()))[:4]
pages = [PageInfo.load(path=path) for path in image_paths]
comic = ComicInfo.from_pages(pages=pages, title='Count Test')
assert len(comic.pages) == 4
comic = ComicInfo.from_pages(pages=pages, title="Count Test")
assert len(comic) == 4
def test_empty_pages_list(self) -> None:
"""Test creating comic with empty pages list."""
comic = ComicInfo.from_pages(pages=[], title='Empty Test')
"""Create a comic with no pages."""
comic = ComicInfo.from_pages(pages=[], title="Empty Test")
assert comic.title == "Empty Test"
assert len(comic) == 0
assert comic.title == 'Empty Test'
assert len(comic.pages) == 0
def test_single_page_comic_load(self, images_dir):
"""Test loading comic with a single page."""
def test_single_page_comic_load(self, images_dir: Path) -> None:
"""Round-trip load of a single-page comic."""
image_paths = sorted(list(images_dir.iterdir()))[:1]
pages = [PageInfo.load(path=path) for path in image_paths]
@@ -168,12 +157,65 @@ class TestComicInfo:
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir) / "single_page.cbz"
data = comic.pack()
with open(temp_path, "wb") as f:
f.write(data)
comic.save(temp_path)
assert temp_path.exists()
comic_loaded = ComicInfo.from_cbz(temp_path)
assert comic_loaded.title == "Single Page Test"
assert len(comic_loaded.pages) == 1
loaded = ComicInfo.from_cbz(temp_path)
assert loaded.title == "Single Page Test"
assert len(loaded) == 1
def test_sequence_protocol(self, images_dir: Path) -> None:
"""Verify sequence protocol (iteration, indexing)."""
image_paths = sorted(list(images_dir.iterdir()))[:3]
pages = [PageInfo.load(path=path) for path in image_paths]
comic = ComicInfo.from_pages(pages=pages, title="Sequence Test")
# Iteration
count = 0
for page in comic:
assert isinstance(page, PageInfo)
count += 1
assert count == 3
# Indexing
first = comic[0]
assert isinstance(first, PageInfo)
last = comic[-1]
assert isinstance(last, PageInfo)
# Slicing
subset = comic[0:2]
assert len(subset) == 2
# Containment
assert first in comic
def test_get_info(self, images_dir: Path) -> None:
"""Verify metadata serialization."""
image_paths = sorted(list(images_dir.iterdir()))[:2]
pages = [PageInfo.load(path=path) for path in image_paths]
comic = ComicInfo.from_pages(
pages=pages,
title="Info Test",
series="Test Series",
year=2024,
)
info = comic.get_info()
assert info["Title"] == "Info Test"
assert info["Series"] == "Test Series"
assert info["Year"] == 2024
assert info["PageCount"] == 2
assert "Pages" in info
assert len(info["Pages"]["Page"]) == 2
def test_none_defaults(self) -> None:
"""Verify optional fields default to None."""
comic = ComicInfo.from_pages(pages=[])
assert comic.number is None
assert comic.count is None
assert comic.volume is None
assert comic.year is None
assert comic.community_rating is None
+133 -194
View File
@@ -1,265 +1,204 @@
from cbz.models import BaseModel, ComicModel, PageModel
from cbz.constants import Format, YesNo, Manga, AgeRating, PageType
"""Tests for data models."""
from dataclasses import fields
class TestBaseModel:
"""Test cases for BaseModel class."""
import pytest
def test_base_model_creation(self) -> None:
"""Test creating BaseModel with fields."""
test_fields = {
'test_str': ('Test String', str),
'test_int': ('Test Integer', int),
'test_bool': ('Test Boolean', bool)
}
model = BaseModel(fields=test_fields)
# Check default values are set
assert hasattr(model, 'test_str')
assert hasattr(model, 'test_int')
assert hasattr(model, 'test_bool')
def test_base_model_with_kwargs(self) -> None:
"""Test creating BaseModel with keyword arguments."""
test_fields = {
'title': ('Title', str),
'number': ('Number', int),
'published': ('Published', bool)
}
model = BaseModel(
fields=test_fields,
title='Test Title',
number=42,
published=True
)
assert model.title == 'Test Title'
assert model.number == 42
assert model.published
def test_attribute_type_verification(self) -> None:
"""Test that attribute types are verified on assignment."""
test_fields = {
'count': ('Count', int),
'name': ('Name', str)
}
model = BaseModel(fields=test_fields)
# Valid assignments
model.count = 10
model.name = 'Test'
assert model.count == 10
assert model.name == 'Test'
def test_repr_method(self) -> None:
"""Test string representation of BaseModel."""
test_fields = {
'title': ('Title', str)
}
model = BaseModel(fields=test_fields, title='Test')
repr_str = repr(model)
assert isinstance(repr_str, str)
assert 'BaseModel' in repr_str
from cbz.constants import (
AgeRating,
Format,
LanguageISO,
Manga,
PageType,
Rating,
YesNo,
)
from cbz.models import ComicModel, PageModel, _get_xml_mapping
class TestComicModel:
"""Test cases for ComicModel class."""
"""Tests for the ComicModel dataclass."""
def test_comic_model_creation(self) -> None:
"""Test creating ComicModel with default values."""
def test_default_values(self) -> None:
"""Correct default values."""
model = ComicModel()
# Check that comic-specific attributes exist
assert hasattr(model, 'title')
assert hasattr(model, 'series')
assert hasattr(model, 'number')
assert hasattr(model, 'volume')
assert hasattr(model, 'year')
assert hasattr(model, 'month')
assert hasattr(model, 'day')
assert model.title == ""
assert model.series == ""
assert model.number is None
assert model.count is None
assert model.volume is None
assert model.year is None
assert model.format == Format.UNKNOWN
assert model.black_white == YesNo.UNKNOWN
assert model.manga == Manga.UNKNOWN
assert model.age_rating == AgeRating.UNKNOWN
assert model.community_rating is None
def test_comic_model_with_values(self) -> None:
"""Test creating ComicModel with specific values."""
def test_with_values(self) -> None:
"""Creation with specific values."""
model = ComicModel(
title='Test Comic',
series='Test Series',
title="Test Comic",
series="Test Series",
number=1,
volume=1,
year=2024,
month=6,
day=15,
writer='Test Writer',
publisher='Test Publisher',
language_iso='en',
writer="Test Writer",
publisher="Test Publisher",
language_iso=LanguageISO("en"),
format=Format.SERIES,
black_white=YesNo.NO,
manga=Manga.RIGHT_LEFT,
age_rating=AgeRating.EVERYONE
manga=Manga.YES_AND_RIGHT_TO_LEFT,
age_rating=AgeRating.EVERYONE,
)
assert model.title == 'Test Comic'
assert model.series == 'Test Series'
assert model.title == "Test Comic"
assert model.series == "Test Series"
assert model.number == 1
assert model.volume == 1
assert model.year == 2024
assert model.month == 6
assert model.day == 15
assert model.writer == 'Test Writer'
assert model.publisher == 'Test Publisher'
assert model.language_iso == 'en'
assert model.format == Format.SERIES
assert model.black_white == YesNo.NO
assert model.manga == Manga.RIGHT_LEFT
assert model.age_rating == AgeRating.EVERYONE
assert model.manga == Manga.YES_AND_RIGHT_TO_LEFT
def test_comic_model_enum_properties(self) -> None:
"""Test that enum properties work correctly."""
def test_enum_assignment(self) -> None:
"""Enum assignment."""
model = ComicModel()
# Test format enum
model.format = Format.PREVIEW
assert model.format == Format.PREVIEW
# Test yes/no enum
model.black_white = YesNo.YES
assert model.black_white == YesNo.YES
# Test manga enum
model.manga = Manga.RIGHT_LEFT
assert model.manga == Manga.RIGHT_LEFT
model.manga = Manga.YES_AND_RIGHT_TO_LEFT
assert model.manga == Manga.YES_AND_RIGHT_TO_LEFT
# Test age rating enum
model.age_rating = AgeRating.TEEN
assert model.age_rating == AgeRating.TEEN
def test_comic_model_metadata_fields(self) -> None:
"""Test comic metadata fields."""
def test_metadata_fields(self) -> None:
"""Verify metadata fields."""
model = ComicModel(
summary='Test summary',
notes='Test notes',
genre='Adventure',
web='http://example.com',
ean='1234567890123',
community_rating=5,
main_character_or_team='Hero',
characters='Hero, Villain',
teams='Justice League',
locations='Metropolis',
scan_information='Scanned by Test',
story_arc='Origin Story',
series_group='DC Comics',
alternate_series='Alternate Universe',
summary="Test summary",
notes="Test notes",
genre="Adventure",
web="http://example.com",
ean="1234567890123",
community_rating=Rating(5),
main_character_or_team="Hero",
characters="Hero, Villain",
teams="Justice League",
locations="Metropolis",
scan_information="Scanned by Test",
story_arc="Origin Story",
series_group="DC Comics",
alternate_series="Alternate Universe",
alternate_number=2,
alternate_count=10
alternate_count=10,
)
assert model.summary == 'Test summary'
assert model.notes == 'Test notes'
assert model.genre == 'Adventure'
assert model.web == 'http://example.com'
assert model.ean == '1234567890123'
assert model.summary == "Test summary"
assert model.genre == "Adventure"
assert model.community_rating == 5
assert model.main_character_or_team == 'Hero'
assert model.characters == 'Hero, Villain'
assert model.teams == 'Justice League'
assert model.locations == 'Metropolis'
assert model.scan_information == 'Scanned by Test'
assert model.story_arc == 'Origin Story'
assert model.series_group == 'DC Comics'
assert model.alternate_series == 'Alternate Universe'
assert model.characters == "Hero, Villain"
assert model.alternate_number == 2
assert model.alternate_count == 10
def test_xml_mapping(self) -> None:
"""Verify XML mapping."""
mapping = _get_xml_mapping(ComicModel)
assert "title" in mapping
assert mapping["title"][0] == "Title"
def test_all_fields_have_xml_mapping(self) -> None:
"""All annotated fields have an XML mapping."""
mapping = _get_xml_mapping(ComicModel)
for f in fields(ComicModel):
if "xml_name" in f.metadata:
assert f.name in mapping
class TestPageModel:
"""Test cases for PageModel class."""
"""Tests for the PageModel dataclass."""
def test_page_model_creation(self) -> None:
"""Test creating PageModel with default values."""
def test_default_values(self) -> None:
"""Correct default values."""
model = PageModel()
# Check that page-specific attributes exist
assert hasattr(model, 'image')
assert hasattr(model, 'type')
assert hasattr(model, 'double')
assert hasattr(model, 'image_size')
assert hasattr(model, 'key')
assert hasattr(model, 'bookmark')
assert hasattr(model, 'image_width')
assert hasattr(model, 'image_height')
assert hasattr(model, 'image_size')
# Note: format is not a base field in PageModel
assert model.type == PageType.STORY
assert model.double is False
assert model.image_size == 0
assert model.key == ""
assert model.bookmark == ""
assert model.image_width == 0
assert model.image_height == 0
def test_page_model_with_values(self) -> None:
"""Test creating PageModel with specific values."""
def test_with_values(self) -> None:
"""Creation with specific values."""
model = PageModel(
type=PageType.FRONT_COVER,
double=True,
image_size=1024000,
key='cover',
bookmark='Chapter 1',
key="cover",
bookmark="Chapter 1",
image_width=800,
image_height=1200,
)
assert model.type == PageType.FRONT_COVER
assert model.double
assert model.double is True
assert model.image_size == 1024000
assert model.key == 'cover'
assert model.bookmark == 'Chapter 1'
assert model.key == "cover"
assert model.bookmark == "Chapter 1"
assert model.image_width == 800
assert model.image_height == 1200
def test_page_model_page_types(self) -> None:
"""Test different page types."""
page_types = [
PageType.FRONT_COVER,
PageType.INNER_COVER,
PageType.ROUNDUP,
PageType.STORY,
PageType.ADVERTISEMENT,
PageType.EDITORIAL,
PageType.LETTERS,
PageType.PREVIEW,
PageType.BACK_COVER,
PageType.OTHER,
PageType.DELETED
]
for page_type in page_types:
def test_all_page_types(self) -> None:
"""All page types are valid."""
for page_type in PageType:
model = PageModel(type=page_type)
assert model.type == page_type
def test_page_model_boolean_properties(self) -> None:
"""Test boolean properties in PageModel."""
def test_boolean_properties(self) -> None:
"""Boolean double property."""
model = PageModel()
# Test double property
model.double = True
assert model.double
assert model.double is True
model.double = False
assert not model.double
assert model.double is False
def test_page_model_numeric_properties(self) -> None:
"""Test numeric properties in PageModel."""
model = PageModel(
image_size=2048000,
image_width=1920,
image_height=1080
)
assert model.image_size == 2048000
assert model.image_width == 1920
assert model.image_height == 1080
class TestRating:
"""Tests for the Rating type."""
# Test that they're integers
assert isinstance(model.image_size, int)
assert isinstance(model.image_width, int)
assert isinstance(model.image_height, int)
def test_valid_rating(self) -> None:
"""Valid ratings (0-5)."""
assert Rating(0) == 0.0
assert Rating(2.5) == 2.5
assert Rating(5) == 5.0
def test_invalid_rating(self) -> None:
"""Invalid ratings raise ValueError."""
with pytest.raises(ValueError):
Rating(-1)
with pytest.raises(ValueError):
Rating(6)
class TestLanguageISO:
"""Tests for the LanguageISO type."""
def test_valid_language(self) -> None:
"""Valid language codes."""
assert LanguageISO("en") == "en"
assert LanguageISO("fr") == "fr"
assert LanguageISO("ja") == "ja"
def test_empty_language(self) -> None:
"""Empty language code is allowed."""
assert LanguageISO("") == ""
def test_invalid_language(self) -> None:
"""Invalid language code raises ValueError."""
with pytest.raises(ValueError):
LanguageISO("zzzzzzz")
+59 -70
View File
@@ -1,16 +1,19 @@
"""Tests for the PageInfo class."""
from pathlib import Path
import pytest
from cbz.page import PageInfo
from cbz.constants import PageType
from cbz.exceptions import InvalidImageError
from cbz.page import PageInfo
class TestPageInfo:
"""Test cases for PageInfo class."""
"""Tests for page loading, properties and manipulation."""
def test_load_from_file(self, sample_image_path: Path) -> None:
"""Test loading PageInfo from image file."""
"""Load from an image file."""
page = PageInfo.load(path=sample_image_path)
assert page is not None
@@ -20,58 +23,49 @@ class TestPageInfo:
assert page.image_width > 0
assert page.image_height > 0
assert page.image_size > 0
assert page.suffix is not None
assert page.suffix != ""
def test_load_with_page_type(self, sample_image_path: Path) -> None:
"""Test loading PageInfo with specific page type."""
"""Load with a specific page type."""
page = PageInfo.load(path=sample_image_path, type=PageType.FRONT_COVER)
assert page.type == PageType.FRONT_COVER
def test_load_with_custom_name(self, sample_image_path: Path) -> None:
"""Test loading PageInfo with custom name."""
custom_name = 'custom_page.jpg'
page = PageInfo.load(path=sample_image_path, name=custom_name)
assert page.name == custom_name
"""Load with a custom name."""
page = PageInfo.load(path=sample_image_path, name="custom_page.jpg")
assert page.name == "custom_page.jpg"
def test_page_content_property(self, sample_image_path: Path) -> None:
"""Test page content property getter and setter."""
"""Content property and automatic metadata extraction."""
page = PageInfo.load(path=sample_image_path)
original_content = page.content
original = page.content
# Test getter
assert page.content == original_content
assert page.content == original
assert isinstance(page.content, bytes)
# Test that content is properly set and metadata extracted
assert page.image_width > 0
assert page.image_height > 0
assert page.image_size > 0
def test_image_metadata_extraction(self, sample_image_path: Path) -> None:
"""Test that image metadata is correctly extracted."""
"""Correct extraction of image metadata."""
page = PageInfo.load(path=sample_image_path)
# Check that all image metadata properties are set
assert hasattr(page, 'image_width') and page.image_width > 0
assert hasattr(page, 'image_height') and page.image_height > 0
assert hasattr(page, 'image_size') and page.image_size > 0
assert hasattr(page, 'suffix') and page.suffix is not None
# Verify dimensions make sense for an image
assert page.image_width > 0
assert page.image_height > 0
assert page.image_size > 0
assert page.suffix != ""
assert isinstance(page.image_width, int)
assert isinstance(page.image_height, int)
assert isinstance(page.image_size, int)
def test_multiple_image_formats(self, images_dir: Path) -> None:
"""Test loading different image formats."""
image_files = list(images_dir.glob('*.jpg'))
"""Load different image files."""
image_files = list(images_dir.glob("*.jpg"))
if not image_files:
pytest.skip('No image files found in example directory')
pytest.skip("No image files found")
for image_path in image_files[:3]: # Test first 3 images
for image_path in image_files[:3]:
page = PageInfo.load(path=image_path)
assert page is not None
@@ -82,62 +76,57 @@ class TestPageInfo:
assert page.image_size > 0
def test_page_type_assignment(self, sample_image_path: Path) -> None:
"""Test different page type assignments."""
page_types = [
PageType.FRONT_COVER,
PageType.INNER_COVER,
PageType.ROUNDUP,
PageType.STORY,
PageType.ADVERTISEMENT,
PageType.EDITORIAL,
PageType.LETTERS,
PageType.PREVIEW,
PageType.BACK_COVER,
PageType.OTHER,
PageType.DELETED
]
for page_type in page_types:
page = PageInfo.load(path=sample_image_path, type=page_type)
"""Assignment of all page types."""
image_bytes = sample_image_path.read_bytes()
for page_type in PageType:
page = PageInfo.loads(data=image_bytes, type=page_type)
assert page.type == page_type
def test_page_creation_from_bytes(self, sample_image_path: Path) -> None:
"""Test creating PageInfo directly from bytes."""
# Read image file as bytes
with open(sample_image_path, 'rb') as f:
image_bytes = f.read()
"""Direct creation from bytes."""
image_bytes = sample_image_path.read_bytes()
# Create page from bytes
page = PageInfo(content=image_bytes, name='test_page.jpg')
page = PageInfo.loads(data=image_bytes, name="test_page.jpg")
assert page.content == image_bytes
assert page.name == 'test_page.jpg'
assert page.name == "test_page.jpg"
assert page.image_width > 0
assert page.image_height > 0
assert page.image_size > 0
def test_repr_string(self, sample_image_path: Path) -> None:
"""Test string representation of PageInfo."""
page = PageInfo.load(path=sample_image_path, type=PageType.STORY)
repr_str = repr(page)
assert 'PageInfo' in repr_str
assert isinstance(repr_str, str)
def test_page_bookmark_property(self, sample_image_path: Path) -> None:
"""Test page bookmark property."""
# Test with bookmark
page = PageInfo.load(path=sample_image_path, bookmark='Chapter 1')
assert page.bookmark == 'Chapter 1'
"""Bookmark property."""
page = PageInfo.load(path=sample_image_path, bookmark="Chapter 1")
assert page.bookmark == "Chapter 1"
# Test without bookmark
page_no_bookmark = PageInfo.load(path=sample_image_path)
assert hasattr(page_no_bookmark, 'bookmark')
assert page_no_bookmark.bookmark == ""
def test_page_double_page_property(self, sample_image_path: Path) -> None:
"""Test page double_page property."""
"""Double page property."""
page = PageInfo.load(path=sample_image_path, double=True)
assert page.double
assert page.double is True
page_no_double = PageInfo.load(path=sample_image_path)
assert hasattr(page_no_double, 'double')
assert page_no_double.double is False
def test_invalid_data_raises_error(self) -> None:
"""Invalid data raises InvalidImageError."""
with pytest.raises(InvalidImageError):
PageInfo.loads(data=b"not an image")
def test_empty_data_raises_error(self) -> None:
"""Empty data raises InvalidImageError."""
with pytest.raises(InvalidImageError):
PageInfo.loads(data=b" ")
def test_save_and_reload(self, sample_image_path: Path, tmp_path: Path) -> None:
"""Save and reload a page."""
page = PageInfo.load(path=sample_image_path)
save_path = tmp_path / "saved_page.jpg"
page.save(save_path)
reloaded = PageInfo.load(path=save_path)
assert reloaded.image_width == page.image_width
assert reloaded.image_height == page.image_height
assert reloaded.image_size == page.image_size