Skip to content

Commit

Permalink
tests: added nhentai doujin get testing
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexandre Brito committed Jan 26, 2024
1 parent 5f2c0dc commit 76377e2
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 18 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Development Pypi
on:
push:
branches:
- "dev"
- "master"
pull_request:
branches:
- "dev"
- "master"
jobs:
pypi:
name: Code Testing
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@master
with:
python-version: 3.10

- name: Preparing the environment
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then python -m pip install -r requirements.txt; else python -m pip install beautifulsoup4 requests pydantic; fi
- name: Starting Testing Suites
run: |
python -m pytest ./tests
8 changes: 4 additions & 4 deletions enma/application/use_cases/get_manga.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from dataclasses import dataclass
from typing import Union

from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, validator
from enma.application.core.handlers.error import InvalidRequest
from enma.application.core.interfaces.manga_repository import IMangaRepository
from enma.application.core.interfaces.use_case import DTO, IUseCase
from enma.application.core.utils.logger import logger
Expand All @@ -15,15 +16,14 @@ class GetMangaRequestDTO(BaseModel):
class GetMangaResponseDTO:
found: bool
manga: Union[Manga, None]

class GetMangaUseCase(IUseCase[GetMangaRequestDTO, GetMangaResponseDTO]):

def __init__(self, manga_repository: IMangaRepository):
self.__manga_repository = manga_repository

def execute(self, dto: DTO[GetMangaRequestDTO]) -> GetMangaResponseDTO:
logger.info(f'Fetching manga with identifier: {dto.data.identifier}.')

manga = self.__manga_repository.get(identifier=dto.data.identifier,
with_symbolic_links=dto.data.with_symbolic_links)

Expand Down
2 changes: 1 addition & 1 deletion enma/domain/entities/manga.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class SymbolicLink:

@dataclass
class Chapter:
id: str | int
id: str | int = field(default=0)
pages: list[Image] = field(default_factory=list)
pages_count: int = field(default=0)
link: SymbolicLink | None = field(default=None)
Expand Down
9 changes: 7 additions & 2 deletions enma/infra/adapters/repositories/nhentai.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,20 @@ def fetch_chapter_by_symbolic_link(self,

if response.status_code != 200:
self.__handle_request_error(msg=f'Could not fetch {link.link} because nhentai\'s request ends up with {response.status_code} status code.')
return Chapter()

doujin: NHentaiResponse = response.json()

if doujin.get('media_id') is None or doujin.get('images') is None:
return Chapter()

return self.__create_chapter(media_id=doujin.get('media_id'), pages=doujin.get('images').get('pages'))

def __create_chapter(self,
media_id: str,
pages: list[NHentaiImage]) -> Chapter:

chapter = Chapter(id=0)
chapter = Chapter()
for index, page in enumerate(pages):
mime = MIME[page.get('t').upper()]
chapter.add_page(Image(uri=self.__make_page_uri(type='page',
Expand All @@ -128,6 +132,7 @@ def __create_chapter(self,
def get(self,
identifier: str,
with_symbolic_links: bool = False) -> Manga | None:
print(identifier, with_symbolic_links)
url = f'{self.__API_URL}/gallery/{identifier}'
response = self.__make_request(url=url)

Expand All @@ -138,7 +143,7 @@ def get(self,
media_id = doujin.get('media_id')

if with_symbolic_links:
chapter = Chapter(id=0, link=SymbolicLink(link=url))
chapter = Chapter(link=SymbolicLink(link=url))
else:
chapter = self.__create_chapter(media_id=media_id, pages=doujin.get('images').get('pages'))

Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
requests==2.31.0
pytest==7.0.1
beautifulsoup4==4.10.0
beautifulsoup4==4.10.0
pydantic==2.5.3
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ python_requires = >=3.9
install_requires =
requests==2.31.0
beautifulsoup4==4.10.0
pydantic==2.5.3
setup_requires =
setuptools_scm

Expand Down
34 changes: 33 additions & 1 deletion tests/test_nhentai_fetch_chapter_by_symbolic_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,36 @@ def test_fetch_chapter_by_symbolic_link(self):
assert isinstance(response.chapter, Chapter)
assert response.chapter.pages_count == 14
assert response.chapter.id == 0
assert len(response.chapter.pages) == 14
assert len(response.chapter.pages) == 14

def test_should_return_empty_chapter_for_broken_link(self):
with patch('requests.get') as mock_method:
mock = Mock()
mock.status_code = 404
mock.json.return_value = {}
mock_method.return_value = mock

link = SymbolicLink(link='https://nhentai.net')
response = self.sut.execute(dto=DTO(data=FetchChapterBySymbolicLinkRequestDTO(link=link)))

assert isinstance(response.chapter, Chapter)
assert response.chapter.link is None
assert response.chapter.pages_count == 0
assert response.chapter.id == 0
assert len(response.chapter.pages) == 0

def test_should_return_empty_chapter_for_broken_response(self):
with patch('requests.get') as mock_method:
mock = Mock()
mock.status_code = 200
mock.json.return_value = {}
mock_method.return_value = mock

link = SymbolicLink(link='https://nhentai.net')
response = self.sut.execute(dto=DTO(data=FetchChapterBySymbolicLinkRequestDTO(link=link)))

assert isinstance(response.chapter, Chapter)
assert response.chapter.link is None
assert response.chapter.pages_count == 0
assert response.chapter.id == 0
assert len(response.chapter.pages) == 0
94 changes: 85 additions & 9 deletions tests/test_nhentai_get_manga_use_case.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import datetime
import json
from unittest.mock import Mock, patch
from unittest.mock import MagicMock, Mock, patch
from pydantic import ValidationError
import pytest
import sys
import os
from enma.application.core.handlers.error import InvalidRequest

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))

from enma.infra.core.interfaces.nhentai_response import NHentaiResponse
from enma.application.use_cases.get_manga import GetMangaRequestDTO, GetMangaUseCase
from enma.application.core.interfaces.use_case import DTO
from enma.infra.adapters.repositories.nhentai import CloudFlareConfig, NHentai
from enma.domain.entities.manga import MIME, Author, Chapter, Genre, Image, Manga, SymbolicLink, Title
from enma.domain.entities.manga import MIME, Author, Chapter, Genre, Image, Manga, Title

class TestNHentaiGetDoujin:

Expand Down Expand Up @@ -56,6 +59,30 @@ def test_success_doujin_retrieve(self):
assert isinstance(chapter, Chapter)

mock_method.assert_called_with(identifier='489504', with_symbolic_links=False)

def test_must_return_other_titles_as_none_if_doesnt_exists(self):
with patch('requests.get') as mock_method:
mock = Mock()
mock.status_code = 200

with open('./tests/data/get.json', 'r') as get:
data: NHentaiResponse = json.loads(get.read())

data['title']['japanese'] = None
data['title']['pretty'] = None

mock.json.return_value = data

mock_method.return_value = mock

res = self.sut.execute(dto=DTO(data=GetMangaRequestDTO(identifier='489504')))

assert res.found == True
assert res.manga is not None
assert res.manga.id == 1
assert res.manga.title.english == "(C71) [Arisan-Antenna (Koari)] Eat The Rich! (Sukatto Golf Pangya)"
assert res.manga.title.japanese == None
assert res.manga.title.other == None

def test_response_when_it_could_not_get_doujin(self):
with patch('enma.infra.adapters.repositories.nhentai.NHentai.get') as mock_method:
Expand All @@ -79,32 +106,32 @@ def test_return_none_when_not_receive_200_status_code(self):
headers={'User-Agent': ''},
params={},
cookies={'cf_clearance': ''})

def test_return_empty_chapters(self):
with patch('requests.get') as mock_method:
mock = Mock()
mock.status_code = 200

with open('./tests/data/get.json', 'r') as get:
data = json.loads(get.read())
data['images']['pages'] = []
mock.json.return_value = data

mock_method.return_value = mock

doujin = self.sut.execute(dto=DTO(data=GetMangaRequestDTO(identifier='2')))
doujin = self.sut.execute(dto=DTO(data=GetMangaRequestDTO(identifier='1')))

assert doujin.found == True
assert doujin.manga is not None
assert isinstance(doujin.manga.chapters[0], Chapter)
assert len(doujin.manga.chapters[0].pages) == 0
assert doujin.manga.id == 1
assert doujin.manga.id == 1

def test_return_right_mime(self):
with patch('requests.get') as mock_method:
mock = Mock()
mock.status_code = 200

with open('./tests/data/get.json', 'r') as get:
data = json.loads(get.read())
mock.json.return_value = data
Expand All @@ -127,7 +154,7 @@ def test_get_with_symbolic_link(self):
with patch('requests.get') as mock_method:
mock = Mock()
mock.status_code = 200

with open('./tests/data/get.json', 'r') as get:
data = json.loads(get.read())
mock.json.return_value = data
Expand All @@ -141,3 +168,52 @@ def test_get_with_symbolic_link(self):
assert isinstance(doujin.manga.chapters[0], Chapter)
assert doujin.manga.chapters[0].link is not None
assert doujin.manga.chapters[0].link != ""

def test_language_must_be_present(self):
with patch('requests.get') as mock_method:
mock = Mock()
mock.status_code = 200

with open('./tests/data/get.json', 'r') as get:
data = json.loads(get.read())
mock.json.return_value = data

mock_method.return_value = mock

doujin = self.sut.execute(dto=DTO(data=GetMangaRequestDTO(identifier='420719', with_symbolic_links=True)))

assert doujin.found == True
assert doujin.manga is not None
assert doujin.manga.language is not None
assert doujin.manga.language == 'japanese'

def test_images_mime_types_must_be_correct(self):
with patch('requests.get') as mock_method:
mock = Mock()
mock.status_code = 200

with open('./tests/data/get.json', 'r') as get:
data: NHentaiResponse = json.loads(get.read())
mock.json.return_value = data

mock_method.return_value = mock

doujin = self.sut.execute(dto=DTO(data=GetMangaRequestDTO(identifier='420719', with_symbolic_links=True)))

assert doujin.found == True
assert doujin.manga is not None

cover_mime = data['images']['cover']['t']
thumb_mime = data['images']['thumbnail']['t']

assert cover_mime.upper() == doujin.manga.cover.mime.name
assert thumb_mime.upper() == doujin.manga.thumbnail.mime.name

@patch('enma.application.use_cases.get_manga.GetMangaUseCase.execute')
def test_symbolic_links_must_be_disabled_by_default(self, use_case_mock: MagicMock):
self.sut.execute(dto=DTO(data=GetMangaRequestDTO(identifier='420719')))
use_case_mock.assert_called_with(dto=DTO(data=GetMangaRequestDTO(identifier='420719', with_symbolic_links=False)))

def test_must_raise_an_exception_case_user_has_provided_wrong_data_type(self):
with pytest.raises(ValidationError) as _:
self.sut.execute(dto=DTO(data=GetMangaRequestDTO(identifier='420719', with_symbolic_links='nao')))

0 comments on commit 76377e2

Please sign in to comment.