Skip to content

Commit

Permalink
tests: Increase unit test coverage.
Browse files Browse the repository at this point in the history
  • Loading branch information
eli64s committed Dec 4, 2023
1 parent cac9f9b commit 4d7e1eb
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 24 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "readmeai"
version = "0.4.056"
version = "0.4.057"
description = "🚀 Generate beautiful README files from the terminal, powered by OpenAI's GPT language models 💫"
authors = ["Eli <[email protected]>"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion readmeai/core/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ async def generate_text(
except openai.error.OpenAIError as excinfo:
self.logger.error(f"OpenAI Exception:\n{str(excinfo)}")
return await self.null_summary(
index, f"OpenAI exception: {excinfo.response.status_code}"
index, f"OpenAI Exception: {excinfo.response.status_code}"
)

except httpx.HTTPStatusError as excinfo:
Expand Down
84 changes: 73 additions & 11 deletions readmeai/utils/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Utility methods for the readme-ai application."""
"""Utility functions for the readmeai package."""

import re
from pathlib import Path
Expand Down Expand Up @@ -35,8 +35,18 @@ def is_valid_url(url: str) -> bool:
return re.match(regex, url) is not None


def flatten_list(nested_list: List) -> List:
"""Flattens a nested list."""
def flatten_list(nested_list: List[List]) -> List:
"""Flatten a nested list (list of lists converted to a single list).
Parameters
----------
nested_list
The nested list to flatten.
Returns
-------
A flattened list.
"""
result = []
for item in nested_list:
if isinstance(item, list):
Expand All @@ -47,7 +57,21 @@ def flatten_list(nested_list: List) -> List:


def format_sentence(text: str) -> str:
"""Clean and format the generated text from the model."""
"""Formats output text from the LLM model.
i.e. removes extra white space, newlines, tabs, etc.
Parameters
----------
text
The text to format.
Returns
-------
The formatted text, used to generate the README.
"""
# Remove single and double quotes around any text
text = re.sub(r"(?<!\w)['\"](.*?)['\"](?!\w)", r"\1", text)

# Remove newlines and tabs
text = text.replace("\n", "").replace("\t", "")

Expand All @@ -66,26 +90,64 @@ def format_sentence(text: str) -> str:
# Remove extra white space around hyphens
text = re.sub(r"\s*-\s*", "-", text)

return text.strip().strip('"')
return text.strip()


def get_relative_path(absolute_path: str, base_path: str) -> str:
"""Get the relative path of a file."""
"""Get the relative path of a file.
Parameters
----------
absolute_path
Absolute path to the file i.e. the full path on the local machine.
base_path
Base path to use for the relative path i.e. the project root.
Returns
-------
The relative path string of the file.
"""
absolute_path = Path(absolute_path)
return absolute_path.relative_to(base_path)


def remove_substring(input_string: str) -> str:
"""Remove text between HTML tags."""
pattern = r"</p>.*?</div>"
def remove_substring(
input_string: str, pattern: str = r"</p>.*?</div>"
) -> str:
"""Remove a substring from a string.
Parameters
----------
input_string
The string to remove the substring from.
pattern, optional
The substring to remove, by default r"</p>.*?</div>".
Returns
-------
The input string with the substring removed.
"""
output_string = re.sub(
pattern, "</p>\n</div>", input_string, flags=re.DOTALL
)
return output_string


def should_ignore(conf_helper: ConfigHelper, file_path: Path) -> bool:
"""Filters out files that should be ignored."""
"""Check if a file should be ignored and not passed to the LLM API.
Uses a default list of files - 'readmeai/settings/ignore_files.toml'.
Parameters
----------
conf_helper
Configuration helper instance with the ignore files list.
file_path
The path to the file to check.
Returns
-------
True if the file should be ignored, False otherwise.
"""
ignore_files = conf_helper.ignore_files

if (
Expand All @@ -96,7 +158,7 @@ def should_ignore(conf_helper: ConfigHelper, file_path: Path) -> bool:
for directory in ignore_files["directories"]
)
):
logger.debug(f"Ignoring: {file_path.name}")
logger.debug(f"Ignoring item: {file_path.name}")
return True

return False
17 changes: 8 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
"""Pytest configuration file."""

from pathlib import Path

import pytest
import toml

from readmeai.config.settings import AppConfigModel, ConfigHelper, load_config


@pytest.fixture(scope="session")
def config():
current_dir = Path(__file__).parent
parent_dir = current_dir.parent
config_path = parent_dir / "settings/config.toml"
config_dict = load_config()
config_model = AppConfigModel(app=config_dict)
return config_model

with open(config_path, "r") as file:
config = toml.load(file)

return config
@pytest.fixture(scope="session")
def config_helper(config):
return ConfigHelper(config)
4 changes: 2 additions & 2 deletions tests/test_config/test_settings.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""Unit tests for readmeai.settings."""

from readmeai.config.settings import (
GitHost,
BadgeCliOptions,
GitApiUrl,
GitFileUrl,
BadgeCliOptions,
GitHost,
)
from readmeai.utils import utils

Expand Down
69 changes: 69 additions & 0 deletions tests/test_utils/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""Unit tests for utils.py."""

from pathlib import Path

from readmeai.config.settings import ConfigHelper
from readmeai.utils.utils import (
flatten_list,
format_sentence,
get_relative_path,
is_valid_url,
remove_substring,
should_ignore,
)


def test_is_valid_url():
assert is_valid_url("https://www.example.com") is True
assert is_valid_url("http://www.example.com") is True
assert is_valid_url("www.example.com") is False
assert is_valid_url("example.com") is False


def test_flatten_list():
nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened_list = flatten_list(nested_list)
assert flattened_list == [1, 2, 3, 4, 5, 6, 7, 8, 9]


def test_format_sentence():
text = "'hello world'"
formatted_sentence = format_sentence(text)
assert formatted_sentence == "hello world"


def test_get_relative_path():
"""Test that the relative path is correct."""
absolute_path = "readmeai/utils/utils.py"
base_path = Path().parent
relative_path = get_relative_path(absolute_path, base_path)
assert str(relative_path) == "readmeai/utils/utils.py"


def test_remove_substring():
"""Test that the substring is removed from the input string."""
test_string = """
<div>
<p>Paragraph 1 content.</p>
Some content that should remain.
<p>Paragraph 2 content with <div> nested tag.</p></div>
This content is outside of the div tags.
<p>Another paragraph.</p>
<div>Content to be removed.</div>
<p>Final paragraph.</p>
<div>
<p>Paragraph inside div.</p>
Content inside div that should be removed.
</div>
</div>
"""
result = remove_substring(test_string)
assert isinstance(result, str)


def test_should_ignore(config_helper: ConfigHelper):
"""Test that the file is ignored."""
file_path_ignored = Path("README.md")
file_path_not_ignored = Path("readmeai/main.py")
assert should_ignore(config_helper, file_path_ignored) is True
assert should_ignore(config_helper, file_path_not_ignored) is False

0 comments on commit 4d7e1eb

Please sign in to comment.