From 87764cf9b331951bc6af2cd8e166613a4e65f5c8 Mon Sep 17 00:00:00 2001 From: Zach Radford Date: Sun, 2 Nov 2025 15:41:20 -0500 Subject: [PATCH 1/2] Add sample Python code and tests to validate CI/CD workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds test code to validate the GitHub Actions CI pipeline: - Calculator module with basic arithmetic operations - String utilities module with common string functions - Comprehensive pytest test suite covering both modules - Updated requirements.txt with testing and code quality dependencies This enables testing of the CI workflow jobs: - lint-and-type-check: Black, Pylint, MyPy validation - test: Pytest execution with coverage reporting 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- calculator.py | 52 +++++++++++++++++++++++++++ requirements.txt | 12 ++++--- string_utils.py | 32 +++++++++++++++++ tests/__init__.py | 1 + tests/test_calculator.py | 56 +++++++++++++++++++++++++++++ tests/test_string_utils.py | 72 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 calculator.py create mode 100644 string_utils.py create mode 100644 tests/__init__.py create mode 100644 tests/test_calculator.py create mode 100644 tests/test_string_utils.py diff --git a/calculator.py b/calculator.py new file mode 100644 index 0000000..9ea0d19 --- /dev/null +++ b/calculator.py @@ -0,0 +1,52 @@ +""" +A simple calculator module for testing CI/CD workflows. +""" +from typing import Union + + +class Calculator: + """Basic calculator with arithmetic operations.""" + + @staticmethod + def add(a: Union[int, float], b: Union[int, float]) -> Union[int, float]: + """Add two numbers.""" + return a + b + + @staticmethod + def subtract(a: Union[int, float], b: Union[int, float]) -> Union[int, float]: + """Subtract b from a.""" + return a - b + + @staticmethod + def multiply(a: Union[int, float], b: Union[int, float]) -> Union[int, float]: + """Multiply two numbers.""" + return a * b + + @staticmethod + def divide(a: Union[int, float], b: Union[int, float]) -> float: + """Divide a by b.""" + if b == 0: + raise ValueError("Cannot divide by zero") + return a / b + + @staticmethod + def power(a: Union[int, float], b: Union[int, float]) -> Union[int, float]: + """Raise a to the power of b.""" + return a**b + + +def main() -> None: + """Demo usage of the calculator.""" + calc = Calculator() + + print("Calculator Demo") + print("=" * 40) + print(f"5 + 3 = {calc.add(5, 3)}") + print(f"10 - 4 = {calc.subtract(10, 4)}") + print(f"6 * 7 = {calc.multiply(6, 7)}") + print(f"15 / 3 = {calc.divide(15, 3)}") + print(f"2 ^ 8 = {calc.power(2, 8)}") + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt index f1ae579..d1480a8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,8 @@ -# Add your Python dependencies here -# Example: -# requests>=2.28.0 -# flask>=2.3.0 +# Testing dependencies +pytest>=7.4.0 +pytest-cov>=4.1.0 + +# Code quality tools +black>=23.0.0 +pylint>=3.0.0 +mypy>=1.5.0 diff --git a/string_utils.py b/string_utils.py new file mode 100644 index 0000000..5ba8dfc --- /dev/null +++ b/string_utils.py @@ -0,0 +1,32 @@ +""" +String utility functions for testing CI/CD workflows. +""" + + +def reverse_string(text: str) -> str: + """Reverse a string.""" + return text[::-1] + + +def is_palindrome(text: str) -> bool: + """Check if a string is a palindrome.""" + cleaned = "".join(c.lower() for c in text if c.isalnum()) + return cleaned == cleaned[::-1] + + +def count_vowels(text: str) -> int: + """Count the number of vowels in a string.""" + vowels = "aeiouAEIOU" + return sum(1 for char in text if char in vowels) + + +def title_case(text: str) -> str: + """Convert string to title case.""" + return " ".join(word.capitalize() for word in text.split()) + + +def truncate(text: str, max_length: int, suffix: str = "...") -> str: + """Truncate string to max length with optional suffix.""" + if len(text) <= max_length: + return text + return text[: max_length - len(suffix)] + suffix diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..46816dd --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests package.""" diff --git a/tests/test_calculator.py b/tests/test_calculator.py new file mode 100644 index 0000000..35fcde7 --- /dev/null +++ b/tests/test_calculator.py @@ -0,0 +1,56 @@ +""" +Tests for the calculator module. +""" +import pytest +from calculator import Calculator + + +class TestCalculator: + """Test suite for Calculator class.""" + + def test_add_positive_numbers(self): + """Test addition of positive numbers.""" + assert Calculator.add(5, 3) == 8 + assert Calculator.add(10, 20) == 30 + + def test_add_negative_numbers(self): + """Test addition with negative numbers.""" + assert Calculator.add(-5, -3) == -8 + assert Calculator.add(-10, 5) == -5 + + def test_subtract(self): + """Test subtraction.""" + assert Calculator.subtract(10, 3) == 7 + assert Calculator.subtract(5, 10) == -5 + + def test_multiply(self): + """Test multiplication.""" + assert Calculator.multiply(5, 3) == 15 + assert Calculator.multiply(-2, 4) == -8 + assert Calculator.multiply(0, 100) == 0 + + def test_divide(self): + """Test division.""" + assert Calculator.divide(10, 2) == 5.0 + assert Calculator.divide(7, 2) == 3.5 + + def test_divide_by_zero(self): + """Test that division by zero raises ValueError.""" + with pytest.raises(ValueError, match="Cannot divide by zero"): + Calculator.divide(10, 0) + + def test_power(self): + """Test power operation.""" + assert Calculator.power(2, 3) == 8 + assert Calculator.power(5, 2) == 25 + assert Calculator.power(10, 0) == 1 + + def test_add_floats(self): + """Test addition with floating point numbers.""" + result = Calculator.add(0.1, 0.2) + assert pytest.approx(result, 0.001) == 0.3 + + def test_multiply_by_zero(self): + """Test multiplication by zero.""" + assert Calculator.multiply(100, 0) == 0 + assert Calculator.multiply(0, 0) == 0 diff --git a/tests/test_string_utils.py b/tests/test_string_utils.py new file mode 100644 index 0000000..d99a0a7 --- /dev/null +++ b/tests/test_string_utils.py @@ -0,0 +1,72 @@ +""" +Tests for string utility functions. +""" +import pytest +from string_utils import ( + reverse_string, + is_palindrome, + count_vowels, + title_case, + truncate, +) + + +class TestStringUtils: + """Test suite for string utility functions.""" + + def test_reverse_string(self): + """Test string reversal.""" + assert reverse_string("hello") == "olleh" + assert reverse_string("Python") == "nohtyP" + assert reverse_string("") == "" + assert reverse_string("a") == "a" + + def test_is_palindrome_simple(self): + """Test palindrome detection with simple strings.""" + assert is_palindrome("racecar") is True + assert is_palindrome("hello") is False + assert is_palindrome("A") is True + + def test_is_palindrome_with_spaces(self): + """Test palindrome detection ignoring spaces and case.""" + assert is_palindrome("A man a plan a canal Panama") is True + assert is_palindrome("race a car") is False + + def test_is_palindrome_empty(self): + """Test palindrome with empty string.""" + assert is_palindrome("") is True + + def test_count_vowels(self): + """Test vowel counting.""" + assert count_vowels("hello") == 2 + assert count_vowels("AEIOU") == 5 + assert count_vowels("xyz") == 0 + assert count_vowels("") == 0 + assert count_vowels("Python Programming") == 4 + + def test_title_case(self): + """Test title case conversion.""" + assert title_case("hello world") == "Hello World" + assert title_case("python programming") == "Python Programming" + assert title_case("ALREADY UPPER") == "Already Upper" + assert title_case("") == "" + + def test_truncate_short_string(self): + """Test truncation with string shorter than max length.""" + assert truncate("hello", 10) == "hello" + assert truncate("test", 4) == "test" + + def test_truncate_long_string(self): + """Test truncation with string longer than max length.""" + assert truncate("hello world", 8) == "hello..." + assert truncate("this is a long string", 10) == "this is..." + + def test_truncate_custom_suffix(self): + """Test truncation with custom suffix.""" + assert truncate("hello world", 8, suffix=">>") == "hello >> + assert truncate("testing", 5, suffix="!") == "test!" + + def test_truncate_edge_cases(self): + """Test truncation edge cases.""" + assert truncate("", 5) == "" + assert truncate("hi", 2) == "hi" From bfc9e6bbf5f0de3012b47098c52f1fcecf144316 Mon Sep 17 00:00:00 2001 From: Zach Radford Date: Sun, 2 Nov 2025 15:51:12 -0500 Subject: [PATCH 2/2] Fix CI workflow to support both main and master branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workflow was only configured for main and dev branches, but this repository uses master as the default branch. Added master to the branch triggers to ensure CI runs on pull requests and pushes. This provides maximum compatibility and future-proofs the workflow if the repository transitions from master to main later. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b48e2fc..b9256f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [main, dev] + branches: [main, master, dev] pull_request: - branches: [main, dev] + branches: [main, master, dev] jobs: lint-and-type-check: