Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
name: Test

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Install ZSH
run: |
sudo apt-get update
sudo apt-get install -y zsh curl

- name: Test plugin loading
run: |
zsh -c "
source zsh-git-ai.zsh 2>/dev/null || true
if [[ -z \"\$ZSH_GIT_AI_PROVIDER\" ]]; then
echo 'ERROR: Plugin failed to load'
exit 1
fi
echo '✓ Plugin loaded successfully'
echo \" Provider: \$ZSH_GIT_AI_PROVIDER\"
"

- name: Test provider validation
run: |
zsh -c "
source zsh-git-ai.zsh 2>/dev/null || true

# Test anthropic provider validation
export ZSH_GIT_AI_PROVIDER='anthropic'
export ANTHROPIC_API_KEY='test-key'

# Load provider and check requirements
source lib/providers/anthropic.zsh
if anthropic_check_requirements >/dev/null 2>&1; then
echo '✓ Anthropic provider validation passed'
else
echo 'ERROR: Anthropic provider validation failed'
exit 1
fi

# Test openai provider validation
export ZSH_GIT_AI_PROVIDER='openai'
export OPENAI_API_KEY='test-key'
source lib/providers/openai.zsh
if openai_check_requirements >/dev/null 2>&1; then
echo '✓ OpenAI provider validation passed'
else
echo 'ERROR: OpenAI provider validation failed'
exit 1
fi

# Test gemini provider validation
export ZSH_GIT_AI_PROVIDER='gemini'
export GEMINI_API_KEY='test-key'
source lib/providers/gemini.zsh
if gemini_check_requirements >/dev/null 2>&1; then
echo '✓ Gemini provider validation passed'
else
echo 'ERROR: Gemini provider validation failed'
exit 1
fi

# Test ollama provider validation
export ZSH_GIT_AI_PROVIDER='ollama'
source lib/providers/ollama.zsh
# Ollama doesn't require API key, just check loading
echo '✓ Ollama provider loaded'
"

- name: Test load_provider function
run: |
zsh -c "
source zsh-git-ai.zsh 2>/dev/null || true

# Test loading valid provider
export ANTHROPIC_API_KEY='test-key'
if load_provider 'anthropic' >/dev/null 2>&1; then
echo '✓ Valid provider loads successfully'
else
echo 'ERROR: Failed to load valid provider'
exit 1
fi

# Test loading invalid provider
if ! load_provider 'invalid' 2>/dev/null; then
echo '✓ Invalid provider correctly rejected'
else
echo 'ERROR: Invalid provider not rejected'
exit 1
fi
"

- name: Run test suite
run: |
# Make test runner executable
chmod +x run-tests.zsh
# Run all tests
./run-tests.zsh

lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Install shellcheck
run: |
sudo apt-get update
sudo apt-get install -y shellcheck

- name: Run shellcheck
run: |
# Shellcheck all shell scripts (zsh files use bash compatibility mode)
find . -name "*.zsh" -o -name "*.sh" | grep -v temp-zsh-ai | xargs shellcheck -s bash -e SC1090,SC1091,SC2034,SC2154 || true
68 changes: 68 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Project Development Guide for git-commit-ai

## Overview
This is a lightweight ZSH tool that generates git commit messages using various AI providers (Anthropic, OpenAI, Gemini, Ollama). The codebase is pure shell script with zero runtime dependencies.

## Testing

### Running Tests
```bash
# Run all tests
./run-tests.zsh

# Run specific test directory
./run-tests.zsh tests/providers

# Run single test file
zsh tests/providers/anthropic.test.zsh
```

### Test Structure
- Tests are located in `tests/` directory
- Provider-specific tests in `tests/providers/`
- Test helper utilities in `tests/test_helper.zsh`
- Custom ZSH-based testing framework with mocking support

### Writing Tests
- Follow existing test patterns in test files
- Use `test_helper.zsh` functions for assertions and mocking
- Each test should have proper setup/teardown
- Mock external commands (curl, jq) rather than making real API calls

## Development Workflow

### Before Committing
1. Run all tests: `./run-tests.zsh`
2. Verify shellcheck passes (runs automatically in CI)
3. Test your changes manually with different providers
4. Ensure no API keys or sensitive data are committed

### Code Style
- Follow existing ZSH scripting patterns
- Use meaningful variable names with proper scoping
- Add error handling for all external commands
- Keep functions small and focused
- Maintain backwards compatibility with older ZSH versions

### Provider Implementation
When adding/modifying AI providers:
1. Implement provider in `lib/providers/` directory
2. Add comprehensive tests in `tests/providers/`
3. Update documentation if adding new configuration options
4. Test with actual API calls (use your own keys)
5. Ensure provider follows the standard interface:
- `{provider}_check_requirements` function
- `{provider}_generate_commit_message` function accepting diff and status

### Common Tasks
- **Adding a new provider**: Copy existing provider pattern, add tests
- **Modifying commit generation**: Update provider logic and test with various diff formats
- **Changing main script logic**: Update `zsh-git-ai.zsh` and test in `tests/main.test.zsh`

### Important Notes
- This is a shell-only project - no Node.js, Python, or other runtimes
- Keep the codebase lightweight
- Preserve zero-dependency philosophy (jq is optional)
- CI runs on every PR - ensure tests pass before merging
- All providers should gracefully handle missing jq by falling back to sed/grep

99 changes: 99 additions & 0 deletions run-tests.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env zsh

# Test runner for git-commit-ai
# Runs all test files and reports results

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# Counters
TOTAL_TESTS=0
TOTAL_PASSED=0
TOTAL_FAILED=0
FAILED_FILES=()

# Run test file and capture results
run_test_file() {
local test_file="$1"
echo -e "${YELLOW}Running ${test_file}...${NC}"

# Run test and capture output
local output
local exit_code

output=$(zsh "$test_file" 2>&1)
exit_code=$?

# Display output
echo "$output"

# Count test results from output
local passed=$(echo "$output" | grep -c "✓")
local failed=$(echo "$output" | grep -c "✗")

# Update totals
if [[ $passed -gt 0 || $failed -gt 0 ]]; then
TOTAL_TESTS=$((TOTAL_TESTS + passed + failed))
TOTAL_PASSED=$((TOTAL_PASSED + passed))
TOTAL_FAILED=$((TOTAL_FAILED + failed))
fi

# Track failed files
if [[ $exit_code -ne 0 || $failed -gt 0 ]]; then
FAILED_FILES+=("$test_file")
fi

echo ""
}

# Main function
main() {
local test_dir="${1:-tests}"

echo -e "${BLUE}Running git-commit-ai tests...${NC}"
echo ""

# Find all test files
local test_files=($test_dir/**/*.test.zsh(N))

if [[ ${#test_files} -eq 0 ]]; then
echo -e "${YELLOW}No test files found in $test_dir${NC}"
exit 1
fi

# Run each test file
for test_file in $test_files; do
run_test_file "$test_file"
done

# Summary
echo "================================"
echo -e "${BLUE}Test Summary:${NC}"
echo -e " Total Tests: $TOTAL_TESTS"
echo -e " ${GREEN}Passed: $TOTAL_PASSED${NC}"
echo -e " ${RED}Failed: $TOTAL_FAILED${NC}"

if [[ ${#FAILED_FILES} -gt 0 ]]; then
echo -e "\n${RED}Failed test files:${NC}"
for file in $FAILED_FILES; do
echo " - $file"
done
fi
echo "================================"

# Exit based on results
if [[ $TOTAL_FAILED -gt 0 ]]; then
exit 1
else
exit 0
fi
}

# Run if executed directly
if [[ "${ZSH_EVAL_CONTEXT}" == "toplevel" ]]; then
main "$@"
fi
71 changes: 71 additions & 0 deletions tests/main.test.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env zsh

# Load test helper
source "${0:A:h}/test_helper.zsh"

# Test main script functionality
test_default_provider() {
setup_test_env
unset ZSH_GIT_AI_PROVIDER
source "$PLUGIN_DIR/zsh-git-ai.zsh" 2>/dev/null || true
# Default should be anthropic
assert_equals "$ZSH_GIT_AI_PROVIDER" "anthropic"
teardown_test_env
}

test_load_provider_success() {
setup_test_env
export ZSH_GIT_AI_PROVIDER="anthropic"
export ANTHROPIC_API_KEY="test-key"

# Mock the provider check requirements function
anthropic_check_requirements() {
return 0
}

# Source just the load_provider function
source "$PLUGIN_DIR/zsh-git-ai.zsh" 2>/dev/null || true
local result
load_provider "anthropic" >/dev/null 2>&1
result=$?

assert_equals "$result" "0"
teardown_test_env
}

test_load_provider_invalid() {
setup_test_env
export ZSH_GIT_AI_PROVIDER="invalid_provider"

# Source just the load_provider function
source "$PLUGIN_DIR/zsh-git-ai.zsh" 2>/dev/null || true
local output
output=$(load_provider "invalid_provider" 2>&1)
local result=$?

assert_equals "$result" "1"
assert_contains "$output" "Provider 'invalid_provider' not found"
teardown_test_env
}

test_spinner_function_exists() {
setup_test_env
source "$PLUGIN_DIR/zsh-git-ai.zsh" 2>/dev/null || true

# Check if show_spinner function is defined
if type show_spinner >/dev/null 2>&1; then
echo "✓ show_spinner function exists"
else
echo "✗ show_spinner function not found"
return 1
fi

teardown_test_env
}

# Run tests
echo "Testing main script..."
test_default_provider
test_load_provider_success
test_load_provider_invalid
test_spinner_function_exists
Loading