From 5b0cac515745102c1e3555da13b98c40baa2bd92 Mon Sep 17 00:00:00 2001
From: David Luevano Alvarado <david@luevano.xyz>
Date: Mon, 1 May 2023 06:58:27 -0600
Subject: add page class testing

---
 setup.cfg                   |  1 +
 src/pyssg/page.py           | 59 ++++++++++-----------------------
 tests/conftest.py           | 80 ++++++++++++++++++++++++++++++++++++++++++---
 tests/test_configuration.py | 10 +++---
 tests/test_page.py          | 62 +++++++++++++++++++++++++++++++++++
 5 files changed, 161 insertions(+), 51 deletions(-)
 create mode 100644 tests/test_page.py

diff --git a/setup.cfg b/setup.cfg
index bf05793..9fcde08 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -64,6 +64,7 @@ per-file-ignores =
 	custom_logger.py: E501
 	test_database_entry.py: E501
 	test_configuration.py: E501
+	test_page.py: E501
 
 [pbr]
 skip_authors = True
diff --git a/src/pyssg/page.py b/src/pyssg/page.py
index 4902bea..40eb41a 100644
--- a/src/pyssg/page.py
+++ b/src/pyssg/page.py
@@ -7,14 +7,14 @@ log: Logger = getLogger(__name__)
 
 class Page:
     def __init__(self, name: str,
-                       ctime: float,
-                       mtime: float,
-                       html: str,
-                       toc: str,
-                       toc_tokens: list[str],
-                       meta: dict[str, Any],
-                       config: dict[str, Any],
-                       dir_config: dict[str, Any]) -> None:
+                 ctime: float,
+                 mtime: float,
+                 html: str,
+                 toc: str,
+                 toc_tokens: list[str],
+                 meta: dict[str, Any],
+                 config: dict[str, Any],
+                 dir_config: dict[str, Any]) -> None:
         log.debug('initializing a page object with name "%s"', name)
         # initial data
         self.name: str = name
@@ -38,7 +38,6 @@ class Page:
 
         # constructed
         self.url: str
-        self.image_url: str
         self.cdate_rss: str
         self.cdate_sitemap: str
         self.mdate_rss: str | None = None
@@ -60,30 +59,29 @@ class Page:
                       var, or_else)
             return or_else
 
-    def cdate(self, format: str) -> str:
+    # these date/cdate/mdate might be a bit overcomplicated
+
+    def __date(self, dt: datetime, format: str) -> str:
         if format in self.config['fmt']:
-            return self.cdatetime.strftime(self.config['fmt'][format])
+            return dt.strftime(self.config['fmt'][format])
         else:
             log.warning('format "%s" not found in config, returning '
                         'empty string', format)
             return ''
 
+    def cdate(self, format: str) -> str:
+        return self.__date(self.cdatetime, format)
+
     def mdate(self, format: str) -> str:
         if self.mdatetime is None:
             log.warning('no mdatetime found, can\'t return a formatted string')
             return ''
-        if format in self.config['fmt']:
-            return self.mdatetime.strftime(self.config['fmt'][format])
-        else:
-            log.warning('format "%s" not found in config, returning '
-                        'empty string', format)
-            return ''
+        return self.__date(self.mdatetime, format)
 
     def from_timestamp(self, timestamp: float) -> datetime:
         return datetime.fromtimestamp(timestamp, tz=timezone.utc)
 
-    # parses meta from self.meta, for og, it prioritizes,
-    #   the actual og meta
+    # parses meta from self.meta
     def parse_metadata(self):
         log.debug('parsing metadata for file "%s"', self.name)
         self.title = str(self.__get_meta('title'))
@@ -124,26 +122,3 @@ class Page:
         name_html: str = self.name.replace(".md", ".html")
         self.url = f'{self.config["url"]["main"]}/{name_html}'
         log.debug('final url "%s"', self.url)
-
-        log.debug('parsing image url')
-        default_image_url: str = ''
-        if 'default_image' in self.config['url']:
-            log.debug('"default_image" url found, will use if no "image_url" '
-                      'is found')
-            default_image_url = self.config['url']['default_image']
-
-        image_url: str
-        image_url = str(self.__get_meta('image_url', default_image_url))
-
-        if image_url != '':
-            if 'static' in self.config['url']:
-                self.image_url = f'{self.config["url"]["static"]}/{image_url}'
-            else:
-                log.debug('no static url set, using main url')
-                self.image_url = f'{self.config["url"]["main"]}/{image_url}'
-            log.debug('final image url "%s"', self.image_url)
-        else:
-            self.image_url = ''
-            log.debug('no image url set for the page, could be because no'
-                      ' "image_url" was found in the metadata and/or no '
-                      ' "default_image" set in the config file')
diff --git a/tests/conftest.py b/tests/conftest.py
index aaf0b3a..dc92eb1 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -9,10 +9,12 @@ from argparse import ArgumentParser
 from datetime import datetime, timezone
 from importlib.metadata import version as v
 from logging import Logger, getLogger, DEBUG
+from copy import deepcopy
 
 from pyssg.arg_parser import get_parser
 from pyssg.custom_logger import setup_logger
 from pyssg.database_entry import DatabaseEntry
+from pyssg.page import Page
 
 
 @pytest.fixture(scope='session')
@@ -56,7 +58,7 @@ def logger() -> Logger:
     return getLogger(__name__)
 
 
-@pytest.fixture
+@pytest.fixture(scope='function')
 def capture_stdout(monkeypatch: MonkeyPatch) -> dict[str, str | int]:
     buffer: dict[str, str | int] = {'stdout': '', 'write_calls': 0}
 
@@ -68,15 +70,15 @@ def capture_stdout(monkeypatch: MonkeyPatch) -> dict[str, str | int]:
     return buffer
 
 
-@pytest.fixture
+@pytest.fixture(scope='session')
 def get_fmt_time() -> Callable[..., str]:
     def fmt_time(fmt: str) -> str:
         return datetime.now(tz=timezone.utc).strftime(fmt)
     return fmt_time
 
 
-@pytest.fixture
-def default_config_dict() -> dict[str, Any]:
+@pytest.fixture(scope='function')
+def default_config() -> dict[str, Any]:
     return {'define': '$PYSSG_HOME/pyssg/site_example/',
             'title': 'Example site',
             'path': {
@@ -98,6 +100,18 @@ def default_config_dict() -> dict[str, Any]:
                         'sitemap': False}}}}
 
 
+@pytest.fixture(scope='function')
+def root_dir_config() -> dict[str, Any]:
+    return {'plt': 'page.html',
+            'tags': False,
+            'index': False,
+            'rss': False,
+            'sitemap': False,
+            'src': '/tmp/pyssg/pyssg/site_example/src',
+            'dst': '/tmp/pyssg/pyssg/site_example/dst',
+            'url': 'https://example.com'}
+
+
 @pytest.fixture(scope='function')
 def tmp_dir_structure(tmp_path: Path) -> Path:
     root: Path = tmp_path/'dir_structure'
@@ -171,3 +185,61 @@ def tmp_src_dir(tmp_path: Path,
     for f in files:
         shutil.copy2(f'{src_test}/{f}', f'{str(src)}/{f}')
     return src
+
+
+@pytest.fixture(scope='function')
+def page_simple(default_config: dict[str, Any],
+                root_dir_config: dict[str, Any],
+                rss_date_fmt: str,
+                sitemap_date_fmt: str) -> Page:
+    # adding the fmt as it is added on the code before being passed to the page
+    config: dict[str, Any] = deepcopy(default_config)
+    config['fmt']['rss_date'] = rss_date_fmt
+    config['fmt']['sitemap_date'] = sitemap_date_fmt
+
+    return Page(
+        name='simple.md',
+        ctime=1682418172.291993,
+        mtime=0.0,
+        html='<p>Simple converted md with nothing but this text.</p>',
+        toc='<div class="toc">\n<ul></ul>\n</div>\n',
+        toc_tokens=[],
+        meta=dict(),
+        config=config,
+        dir_config=root_dir_config
+    )
+
+
+# this is basically page_simple with extras
+@pytest.fixture(scope='function')
+def page_simple_modified(default_config: dict[str, Any],
+                         root_dir_config: dict[str, Any],
+                         rss_date_fmt: str,
+                         sitemap_date_fmt: str) -> Page:
+    config: dict[str, Any] = deepcopy(default_config)
+    config['fmt']['rss_date'] = rss_date_fmt
+    config['fmt']['sitemap_date'] = sitemap_date_fmt
+
+    dir_config: dict[str, Any] = deepcopy(root_dir_config)
+    dir_config['tags'] = True
+    tags: list[str] = ['blog', 'english', 'etc']
+
+    basic_meta: dict[str, Any] = {
+        'title': 'Example title',
+        'author': 'Single Author',
+        'summary': 'Some summary.',
+        'lang': 'es',
+        'tags': tags
+    }
+
+    return Page(
+        name='simple_modified.md',
+        ctime=1682418300.291993,
+        mtime=1682418350.291993,
+        html='<p>Simple converted md with nothing but this text, modified.</p>',
+        toc='<div class="toc">\n<ul></ul>\n</div>\n',
+        toc_tokens=[],
+        meta=basic_meta,
+        config=config,
+        dir_config=dir_config
+    )
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index 36761eb..1b16441 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -24,11 +24,11 @@ def test_static_config(rss_date_fmt: str,
 
 def test_default_config(sample_files_path: str,
                         default_yaml: str,
-                        default_config_dict: dict[str, Any]) -> None:
+                        default_config: dict[str, Any]) -> None:
     yaml_path: str = f'{sample_files_path}/config/{default_yaml}'
     yaml: list[dict[str, Any]] = get_parsed_config(yaml_path)
     assert len(yaml) == 1
-    assert yaml[0] == default_config_dict
+    assert yaml[0] == default_config
 
 
 def test_default_config_mising_mandatory_key(sample_files_path: str,
@@ -73,12 +73,12 @@ def test_default_config_root_dir(sample_files_path: str,
 # this really just tests that both documents in the yaml file are read,
 #   both documents are the same (the default.yaml)
 def test_multiple_default_config(sample_files_path: str,
-                                 default_config_dict: dict[str, Any]) -> None:
+                                 default_config: dict[str, Any]) -> None:
     yaml_path: str = f'{sample_files_path}/config/multiple_default.yaml'
     yaml: list[dict[str, Any]] = get_parsed_config(yaml_path)
     assert len(yaml) == 2
-    assert yaml[0] == default_config_dict
-    assert yaml[1] == default_config_dict
+    assert yaml[0] == default_config
+    assert yaml[1] == default_config
 
 
 # also, this just tests that the checks for a well formed config file are
diff --git a/tests/test_page.py b/tests/test_page.py
new file mode 100644
index 0000000..0ffaff8
--- /dev/null
+++ b/tests/test_page.py
@@ -0,0 +1,62 @@
+from copy import deepcopy
+from logging import WARNING
+from typing import Any
+from pytest import LogCaptureFixture
+from pyssg.page import Page
+
+
+# TODO: this probably needs more testing, but I'm doing the coverage for now
+
+
+def test_page_basic(page_simple: Page) -> None:
+    page_simple.parse_metadata()
+    assert page_simple.title == ''
+    assert page_simple.author == ['']
+    assert page_simple.summary == ''
+    assert page_simple.lang == 'en'
+    assert page_simple.url == f"{page_simple.dir_config['url']}/{page_simple.name.replace('.md', '.html')}"
+
+
+def test_page_no_mdate(page_simple: Page,
+                       caplog: LogCaptureFixture) -> None:
+    page_simple.parse_metadata()
+    war: tuple[str, int, str] = ('pyssg.page',
+                                 WARNING,
+                                 'no mdatetime found, can\'t return a formatted string')
+    assert page_simple.mdate('date') == ''
+    assert caplog.record_tuples[-1] == war
+
+
+def test_page_no_fmt(page_simple: Page,
+                     caplog: LogCaptureFixture) -> None:
+    page_simple.parse_metadata()
+    war: tuple[str, int, str] = ('pyssg.page',
+                                 WARNING,
+                                 'format "something" not found in config, '
+                                 'returning empty string')
+    assert page_simple.cdate('something') == ''
+    assert caplog.record_tuples[-1] == war
+
+
+def test_page_comparison(page_simple: Page,
+                         page_simple_modified: Page) -> None:
+    assert not page_simple > page_simple_modified
+    assert page_simple < page_simple_modified
+    assert page_simple != page_simple_modified
+
+
+def test_page_modified(page_simple_modified: Page) -> None:
+    page_simple_modified.parse_metadata()
+    meta: dict[str, Any] = deepcopy(page_simple_modified.meta)
+    assert page_simple_modified.title == meta['title']
+    assert page_simple_modified.author == list(meta['author'])
+    assert page_simple_modified.summary == meta['summary']
+    assert page_simple_modified.lang == meta['lang']
+    assert page_simple_modified.url == f"{page_simple_modified.dir_config['url']}/{page_simple_modified.name.replace('.md', '.html')}"
+
+
+def test_page_modified_no_tags(page_simple_modified: Page) -> None:
+    meta: dict[str, Any] = deepcopy(page_simple_modified.meta)
+    meta['tags'] = []
+    page_simple_modified.meta = meta
+    page_simple_modified.parse_metadata()
-- 
cgit v1.2.3-70-g09d2