Fix CI/CD: Add Gitea Actions workflow and fix linting issues
This commit is contained in:
203
tests/conftest.py
Normal file
203
tests/conftest.py
Normal file
@@ -0,0 +1,203 @@
|
||||
"""Test configuration and fixtures."""
|
||||
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def create_python_project(tmp_path: Path) -> Path:
|
||||
"""Create a Python project structure for testing."""
|
||||
src_dir = tmp_path / "src"
|
||||
src_dir.mkdir()
|
||||
|
||||
(src_dir / "__init__.py").write_text('"""Package init."""')
|
||||
|
||||
(src_dir / "main.py").write_text('''"""Main module."""
|
||||
|
||||
def hello():
|
||||
"""Say hello."""
|
||||
print("Hello, World!")
|
||||
|
||||
class Calculator:
|
||||
"""A simple calculator."""
|
||||
|
||||
def add(self, a: int, b: int) -> int:
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
def multiply(self, a: int, b: int) -> int:
|
||||
"""Multiply two numbers."""
|
||||
return a * b
|
||||
''')
|
||||
|
||||
(tmp_path / "requirements.txt").write_text('''requests>=2.31.0
|
||||
click>=8.0.0
|
||||
pytest>=7.0.0
|
||||
''')
|
||||
|
||||
(tmp_path / "pyproject.toml").write_text('''[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "test-project"
|
||||
version = "0.1.0"
|
||||
description = "A test project"
|
||||
requires-python = ">=3.9"
|
||||
|
||||
dependencies = [
|
||||
"requests>=2.31.0",
|
||||
"click>=8.0.0",
|
||||
]
|
||||
''')
|
||||
|
||||
return tmp_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def create_javascript_project(tmp_path: Path) -> Path:
|
||||
"""Create a JavaScript project structure for testing."""
|
||||
(tmp_path / "package.json").write_text('''{
|
||||
"name": "test-js-project",
|
||||
"version": "1.0.0",
|
||||
"description": "A test JavaScript project",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"express": "^4.18.0",
|
||||
"lodash": "^4.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.0.0"
|
||||
}
|
||||
}
|
||||
''')
|
||||
|
||||
(tmp_path / "index.js").write_text('''const express = require('express');
|
||||
const _ = require('lodash');
|
||||
|
||||
function hello() {
|
||||
return 'Hello, World!';
|
||||
}
|
||||
|
||||
class Calculator {
|
||||
add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { hello, Calculator };
|
||||
''')
|
||||
|
||||
return tmp_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def create_go_project(tmp_path: Path) -> Path:
|
||||
"""Create a Go project structure for testing."""
|
||||
(tmp_path / "go.mod").write_text('''module test-go-project
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
)
|
||||
''')
|
||||
|
||||
(tmp_path / "main.go").write_text('''package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func hello() string {
|
||||
return "Hello, World!"
|
||||
}
|
||||
|
||||
type Calculator struct{}
|
||||
|
||||
func (c *Calculator) Add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(hello())
|
||||
}
|
||||
''')
|
||||
|
||||
return tmp_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def create_rust_project(tmp_path: Path) -> Path:
|
||||
"""Create a Rust project structure for testing."""
|
||||
src_dir = tmp_path / "src"
|
||||
src_dir.mkdir()
|
||||
|
||||
(tmp_path / "Cargo.toml").write_text('''[package]
|
||||
name = "test-rust-project"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tokio = { version = "1.0", features = "full" }
|
||||
|
||||
[dev-dependencies]
|
||||
assertions = "0.1"
|
||||
''')
|
||||
|
||||
(src_dir / "main.rs").write_text('''fn hello() -> String {
|
||||
"Hello, World!".to_string()
|
||||
}
|
||||
|
||||
pub struct Calculator;
|
||||
|
||||
impl Calculator {
|
||||
pub fn add(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{}", hello());
|
||||
}
|
||||
''')
|
||||
|
||||
return tmp_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def create_mixed_project(tmp_path: Path) -> Path:
|
||||
"""Create a project with multiple languages for testing."""
|
||||
python_part = tmp_path / "python_part"
|
||||
python_part.mkdir()
|
||||
js_part = tmp_path / "js_part"
|
||||
js_part.mkdir()
|
||||
|
||||
src_dir = python_part / "src"
|
||||
src_dir.mkdir()
|
||||
(src_dir / "__init__.py").write_text('"""Package init."""')
|
||||
(src_dir / "main.py").write_text('''"""Main module."""
|
||||
def hello():
|
||||
print("Hello")
|
||||
''')
|
||||
(python_part / "pyproject.toml").write_text('''[project]
|
||||
name = "test-project"
|
||||
version = "0.1.0"
|
||||
description = "A test project"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = ["requests>=2.31.0"]
|
||||
''')
|
||||
|
||||
(js_part / "package.json").write_text('''{
|
||||
"name": "test-js-project",
|
||||
"version": "1.0.0",
|
||||
"description": "A test JavaScript project"
|
||||
}
|
||||
''')
|
||||
(js_part / "index.js").write_text('''function hello() {
|
||||
return 'Hello';
|
||||
}
|
||||
module.exports = { hello };
|
||||
''')
|
||||
|
||||
return tmp_path
|
||||
75
tests/fixtures/README_EXAMPLE.md
vendored
Normal file
75
tests/fixtures/README_EXAMPLE.md
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
# Example Generated README
|
||||
|
||||
This is an example of a README file that can be generated by Auto README Generator CLI.
|
||||
|
||||
## Overview
|
||||
|
||||
A Python project located at `/example/project` containing multiple files.
|
||||
|
||||
## Supported Languages
|
||||
|
||||
This project uses:
|
||||
- **Python**
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
- `requests` v2.31.0
|
||||
- `click` v8.0.0
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```python
|
||||
from project_name import main
|
||||
main()
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Test suite included
|
||||
- Uses 2 dependencies
|
||||
- Contains 1 classes
|
||||
- Contains 3 functions
|
||||
|
||||
## API Reference
|
||||
|
||||
### `hello()`
|
||||
|
||||
Say hello.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
### `add(a, b)`
|
||||
|
||||
Add two numbers.
|
||||
|
||||
**Parameters:** `a, b`
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
||||
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
||||
4. Push to the branch (`git push origin feature/amazing-feature`)
|
||||
5. Open a Pull Request
|
||||
|
||||
For Python development:
|
||||
- Run tests with `pytest`
|
||||
- Format code with `black` and `isort`
|
||||
- Check types with `mypy`
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
---
|
||||
|
||||
*Generated by Auto README Generator on 2024-01-15*
|
||||
255
tests/test_analyzers.py
Normal file
255
tests/test_analyzers.py
Normal file
@@ -0,0 +1,255 @@
|
||||
"""Tests for code analyzers."""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
from src.auto_readme.analyzers import (
|
||||
PythonAnalyzer,
|
||||
JavaScriptAnalyzer,
|
||||
GoAnalyzer,
|
||||
RustAnalyzer,
|
||||
CodeAnalyzerFactory,
|
||||
)
|
||||
|
||||
|
||||
class TestPythonAnalyzer:
|
||||
"""Tests for PythonAnalyzer."""
|
||||
|
||||
def test_can_analyze_py_file(self):
|
||||
"""Test that analyzer recognizes Python files."""
|
||||
analyzer = PythonAnalyzer()
|
||||
with tempfile.NamedTemporaryFile(suffix=".py", delete=False, mode="w") as f:
|
||||
f.write("def hello():\n pass")
|
||||
f.flush()
|
||||
assert analyzer.can_analyze(Path(f.name))
|
||||
Path(f.name).unlink()
|
||||
|
||||
def test_can_analyze_pyi_file(self):
|
||||
"""Test that analyzer recognizes .pyi stub files."""
|
||||
analyzer = PythonAnalyzer()
|
||||
with tempfile.NamedTemporaryFile(suffix=".pyi", delete=False, mode="w") as f:
|
||||
f.write("def hello() -> None: ...")
|
||||
f.flush()
|
||||
assert analyzer.can_analyze(Path(f.name))
|
||||
Path(f.name).unlink()
|
||||
|
||||
def test_analyze_simple_function(self):
|
||||
"""Test analyzing a simple function."""
|
||||
with tempfile.NamedTemporaryFile(suffix=".py", delete=False, mode="w") as f:
|
||||
f.write('''"""Module docstring."""
|
||||
|
||||
def hello():
|
||||
"""Say hello."""
|
||||
print("Hello, World!")
|
||||
|
||||
class Calculator:
|
||||
"""A calculator class."""
|
||||
|
||||
def add(self, a, b):
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
''')
|
||||
f.flush()
|
||||
file_path = Path(f.name)
|
||||
analyzer = PythonAnalyzer()
|
||||
result = analyzer.analyze(file_path)
|
||||
|
||||
assert len(result["functions"]) == 2 # hello and add
|
||||
assert len(result["classes"]) == 1
|
||||
|
||||
hello_func = next((f for f in result["functions"] if f.name == "hello"), None)
|
||||
assert hello_func is not None
|
||||
assert hello_func.docstring == "Say hello."
|
||||
assert hello_func.parameters == []
|
||||
|
||||
calc_class = result["classes"][0]
|
||||
assert calc_class.name == "Calculator"
|
||||
assert calc_class.docstring == "A calculator class."
|
||||
|
||||
Path(f.name).unlink()
|
||||
|
||||
def test_analyze_with_parameters(self):
|
||||
"""Test analyzing functions with parameters."""
|
||||
with tempfile.NamedTemporaryFile(suffix=".py", delete=False, mode="w") as f:
|
||||
f.write('''def greet(name, greeting="Hello"):
|
||||
"""Greet someone."""
|
||||
return f"{greeting}, {name}!"
|
||||
|
||||
def add_numbers(a: int, b: int) -> int:
|
||||
"""Add two integers."""
|
||||
return a + b
|
||||
''')
|
||||
f.flush()
|
||||
file_path = Path(f.name)
|
||||
analyzer = PythonAnalyzer()
|
||||
result = analyzer.analyze(file_path)
|
||||
|
||||
greet_func = next((f for f in result["functions"] if f.name == "greet"), None)
|
||||
assert greet_func is not None
|
||||
assert "name" in greet_func.parameters
|
||||
assert "greeting" in greet_func.parameters
|
||||
|
||||
Path(f.name).unlink()
|
||||
|
||||
|
||||
class TestJavaScriptAnalyzer:
|
||||
"""Tests for JavaScriptAnalyzer."""
|
||||
|
||||
def test_can_analyze_js_file(self):
|
||||
"""Test that analyzer recognizes JavaScript files."""
|
||||
analyzer = JavaScriptAnalyzer()
|
||||
with tempfile.NamedTemporaryFile(suffix=".js", delete=False, mode="w") as f:
|
||||
f.write("function hello() { return 'hello'; }")
|
||||
f.flush()
|
||||
assert analyzer.can_analyze(Path(f.name))
|
||||
Path(f.name).unlink()
|
||||
|
||||
def test_can_analyze_ts_file(self):
|
||||
"""Test that analyzer recognizes TypeScript files."""
|
||||
analyzer = JavaScriptAnalyzer()
|
||||
with tempfile.NamedTemporaryFile(suffix=".ts", delete=False, mode="w") as f:
|
||||
f.write("const hello = (): string => 'hello';")
|
||||
f.flush()
|
||||
assert analyzer.can_analyze(Path(f.name))
|
||||
Path(f.name).unlink()
|
||||
|
||||
def test_analyze_simple_function(self):
|
||||
"""Test analyzing a simple JavaScript function."""
|
||||
with tempfile.NamedTemporaryFile(suffix=".js", delete=False, mode="w") as f:
|
||||
f.write('''function hello(name) {
|
||||
return "Hello, " + name + "!";
|
||||
}
|
||||
|
||||
class Calculator {
|
||||
add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { hello, Calculator };
|
||||
''')
|
||||
f.flush()
|
||||
file_path = Path(f.name)
|
||||
analyzer = JavaScriptAnalyzer()
|
||||
result = analyzer.analyze(file_path)
|
||||
|
||||
assert len(result["functions"]) >= 1
|
||||
|
||||
hello_func = next((f for f in result["functions"] if f.name == "hello"), None)
|
||||
assert hello_func is not None
|
||||
|
||||
Path(f.name).unlink()
|
||||
|
||||
|
||||
class TestGoAnalyzer:
|
||||
"""Tests for GoAnalyzer."""
|
||||
|
||||
def test_can_analyze_go_file(self):
|
||||
"""Test that analyzer recognizes Go files."""
|
||||
analyzer = GoAnalyzer()
|
||||
with tempfile.NamedTemporaryFile(suffix=".go", delete=False, mode="w") as f:
|
||||
f.write("package main\n\nfunc hello() string { return 'hello' }")
|
||||
f.flush()
|
||||
assert analyzer.can_analyze(Path(f.name))
|
||||
Path(f.name).unlink()
|
||||
|
||||
def test_analyze_simple_function(self):
|
||||
"""Test analyzing a simple Go function."""
|
||||
with tempfile.NamedTemporaryFile(suffix=".go", delete=False, mode="w") as f:
|
||||
f.write('''package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func hello(name string) string {
|
||||
return "Hello, " + name
|
||||
}
|
||||
|
||||
type Calculator struct{}
|
||||
|
||||
func (c *Calculator) Add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
''')
|
||||
f.flush()
|
||||
file_path = Path(f.name)
|
||||
analyzer = GoAnalyzer()
|
||||
result = analyzer.analyze(file_path)
|
||||
|
||||
hello_func = next((f for f in result["functions"] if f.name == "hello"), None)
|
||||
assert hello_func is not None
|
||||
assert hello_func.return_type is not None or "string" in str(hello_func.return_type)
|
||||
|
||||
Path(f.name).unlink()
|
||||
|
||||
|
||||
class TestRustAnalyzer:
|
||||
"""Tests for RustAnalyzer."""
|
||||
|
||||
def test_can_analyze_rs_file(self):
|
||||
"""Test that analyzer recognizes Rust files."""
|
||||
analyzer = RustAnalyzer()
|
||||
with tempfile.NamedTemporaryFile(suffix=".rs", delete=False, mode="w") as f:
|
||||
f.write("fn hello() -> String { String::from('hello') }")
|
||||
f.flush()
|
||||
assert analyzer.can_analyze(Path(f.name))
|
||||
Path(f.name).unlink()
|
||||
|
||||
def test_analyze_simple_function(self):
|
||||
"""Test analyzing a simple Rust function."""
|
||||
with tempfile.NamedTemporaryFile(suffix=".rs", delete=False, mode="w") as f:
|
||||
f.write('''fn hello(name: &str) -> String {
|
||||
format!("Hello, {}", name)
|
||||
}
|
||||
|
||||
pub struct Calculator;
|
||||
|
||||
impl Calculator {
|
||||
pub fn add(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
}
|
||||
''')
|
||||
f.flush()
|
||||
file_path = Path(f.name)
|
||||
analyzer = RustAnalyzer()
|
||||
result = analyzer.analyze(file_path)
|
||||
|
||||
hello_func = next((f for f in result["functions"] if f.name == "hello"), None)
|
||||
assert hello_func is not None
|
||||
assert "name" in hello_func.parameters
|
||||
assert hello_func.visibility == "private"
|
||||
|
||||
Path(f.name).unlink()
|
||||
|
||||
|
||||
class TestCodeAnalyzerFactory:
|
||||
"""Tests for CodeAnalyzerFactory."""
|
||||
|
||||
def test_get_analyzer_python(self):
|
||||
"""Test getting analyzer for Python file."""
|
||||
analyzer = CodeAnalyzerFactory.get_analyzer(Path("main.py"))
|
||||
assert isinstance(analyzer, PythonAnalyzer)
|
||||
|
||||
def test_get_analyzer_js(self):
|
||||
"""Test getting analyzer for JavaScript file."""
|
||||
analyzer = CodeAnalyzerFactory.get_analyzer(Path("index.js"))
|
||||
assert isinstance(analyzer, JavaScriptAnalyzer)
|
||||
|
||||
def test_get_analyzer_go(self):
|
||||
"""Test getting analyzer for Go file."""
|
||||
analyzer = CodeAnalyzerFactory.get_analyzer(Path("main.go"))
|
||||
assert isinstance(analyzer, GoAnalyzer)
|
||||
|
||||
def test_get_analyzer_rust(self):
|
||||
"""Test getting analyzer for Rust file."""
|
||||
analyzer = CodeAnalyzerFactory.get_analyzer(Path("main.rs"))
|
||||
assert isinstance(analyzer, RustAnalyzer)
|
||||
|
||||
def test_can_analyze(self):
|
||||
"""Test can_analyze returns correct results."""
|
||||
assert CodeAnalyzerFactory.can_analyze(Path("main.py"))
|
||||
assert CodeAnalyzerFactory.can_analyze(Path("index.js"))
|
||||
assert CodeAnalyzerFactory.can_analyze(Path("main.go"))
|
||||
assert CodeAnalyzerFactory.can_analyze(Path("main.rs"))
|
||||
assert not CodeAnalyzerFactory.can_analyze(Path("random.txt"))
|
||||
134
tests/test_cli.py
Normal file
134
tests/test_cli.py
Normal file
@@ -0,0 +1,134 @@
|
||||
"""Tests for CLI commands."""
|
||||
|
||||
|
||||
from click.testing import CliRunner
|
||||
|
||||
from src.auto_readme.cli import generate, preview, analyze, init_config
|
||||
|
||||
|
||||
class TestGenerateCommand:
|
||||
"""Tests for the generate command."""
|
||||
|
||||
def test_generate_basic_python(self, create_python_project, tmp_path):
|
||||
"""Test basic README generation for Python project."""
|
||||
from src.auto_readme.cli import generate
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(generate, ["--input", str(create_python_project), "--output", str(tmp_path / "README.md")])
|
||||
|
||||
assert result.exit_code == 0
|
||||
|
||||
readme_content = (tmp_path / "README.md").read_text()
|
||||
assert "# test-project" in readme_content
|
||||
|
||||
def test_generate_dry_run(self, create_python_project):
|
||||
"""Test README generation with dry-run option."""
|
||||
runner = CliRunner()
|
||||
|
||||
result = runner.invoke(generate, ["--input", str(create_python_project), "--dry-run"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "# test-project" in result.output
|
||||
|
||||
def test_generate_force_overwrite(self, create_python_project, tmp_path):
|
||||
"""Test forced README overwrite."""
|
||||
readme_file = tmp_path / "README.md"
|
||||
readme_file.write_text("# Existing README")
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(generate, ["--input", str(create_python_project), "--output", str(readme_file), "--force"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert readme_file.read_text() != "# Existing README"
|
||||
|
||||
def test_generate_with_template(self, create_python_project):
|
||||
"""Test README generation with specific template."""
|
||||
runner = CliRunner()
|
||||
|
||||
result = runner.invoke(generate, ["--input", str(create_python_project), "--template", "base", "--dry-run"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
class TestPreviewCommand:
|
||||
"""Tests for the preview command."""
|
||||
|
||||
def test_preview_python_project(self, create_python_project):
|
||||
"""Test previewing README for Python project."""
|
||||
runner = CliRunner()
|
||||
|
||||
result = runner.invoke(preview, ["--input", str(create_python_project)])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "# test-project" in result.output
|
||||
|
||||
|
||||
class TestAnalyzeCommand:
|
||||
"""Tests for the analyze command."""
|
||||
|
||||
def test_analyze_python_project(self, create_python_project):
|
||||
"""Test analyzing Python project."""
|
||||
runner = CliRunner()
|
||||
|
||||
result = runner.invoke(analyze, [str(create_python_project)])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "test-project" in result.output
|
||||
assert "Type: python" in result.output
|
||||
|
||||
def test_analyze_js_project(self, create_javascript_project):
|
||||
"""Test analyzing JavaScript project."""
|
||||
runner = CliRunner()
|
||||
|
||||
result = runner.invoke(analyze, [str(create_javascript_project)])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "Type: javascript" in result.output
|
||||
|
||||
def test_analyze_go_project(self, create_go_project):
|
||||
"""Test analyzing Go project."""
|
||||
runner = CliRunner()
|
||||
|
||||
result = runner.invoke(analyze, [str(create_go_project)])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "Type: go" in result.output
|
||||
|
||||
def test_analyze_rust_project(self, create_rust_project):
|
||||
"""Test analyzing Rust project."""
|
||||
runner = CliRunner()
|
||||
|
||||
result = runner.invoke(analyze, [str(create_rust_project)])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "Type: rust" in result.output
|
||||
|
||||
|
||||
class TestInitConfigCommand:
|
||||
"""Tests for the init-config command."""
|
||||
|
||||
def test_init_config(self, tmp_path):
|
||||
"""Test generating configuration template."""
|
||||
runner = CliRunner()
|
||||
|
||||
result = runner.invoke(init_config, ["--output", str(tmp_path / ".readmerc")])
|
||||
|
||||
assert result.exit_code == 0
|
||||
|
||||
config_content = (tmp_path / ".readmerc").read_text()
|
||||
assert "project_name:" in config_content
|
||||
assert "description:" in config_content
|
||||
|
||||
def test_init_config_default_path(self, tmp_path):
|
||||
"""Test generating configuration at default path."""
|
||||
import os
|
||||
original_dir = os.getcwd()
|
||||
try:
|
||||
os.chdir(tmp_path)
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(init_config, ["--output", ".readmerc"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert (tmp_path / ".readmerc").exists()
|
||||
finally:
|
||||
os.chdir(original_dir)
|
||||
132
tests/test_config.py
Normal file
132
tests/test_config.py
Normal file
@@ -0,0 +1,132 @@
|
||||
"""Tests for configuration loading."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from src.auto_readme.config import ConfigLoader, ConfigValidator, ReadmeConfig
|
||||
|
||||
|
||||
class TestConfigLoader:
|
||||
"""Tests for ConfigLoader."""
|
||||
|
||||
def test_find_config_yaml(self, tmp_path):
|
||||
"""Test finding YAML configuration file."""
|
||||
(tmp_path / ".readmerc.yaml").write_text("project_name: test")
|
||||
|
||||
config_path = ConfigLoader.find_config(tmp_path)
|
||||
assert config_path is not None
|
||||
assert config_path.name == ".readmerc.yaml"
|
||||
|
||||
def test_find_config_yml(self, tmp_path):
|
||||
"""Test finding YML configuration file."""
|
||||
(tmp_path / ".readmerc.yml").write_text("project_name: test")
|
||||
|
||||
config_path = ConfigLoader.find_config(tmp_path)
|
||||
assert config_path is not None
|
||||
|
||||
def test_find_config_toml(self, tmp_path):
|
||||
"""Test finding TOML configuration file."""
|
||||
(tmp_path / ".readmerc").write_text("project_name = 'test'")
|
||||
|
||||
config_path = ConfigLoader.find_config(tmp_path)
|
||||
assert config_path is not None
|
||||
|
||||
def test_load_yaml_config(self, tmp_path):
|
||||
"""Test loading YAML configuration file."""
|
||||
config_file = tmp_path / ".readmerc.yaml"
|
||||
config_file.write_text("""
|
||||
project_name: "My Test Project"
|
||||
description: "A test description"
|
||||
template: "minimal"
|
||||
interactive: true
|
||||
|
||||
sections:
|
||||
order:
|
||||
- title
|
||||
- description
|
||||
- overview
|
||||
|
||||
custom_fields:
|
||||
author: "Test Author"
|
||||
email: "test@example.com"
|
||||
""")
|
||||
|
||||
config = ConfigLoader.load(config_file)
|
||||
|
||||
assert config.project_name == "My Test Project"
|
||||
assert config.description == "A test description"
|
||||
assert config.template == "minimal"
|
||||
assert config.interactive is True
|
||||
assert "author" in config.custom_fields
|
||||
|
||||
def test_load_toml_config(self, tmp_path):
|
||||
"""Test loading TOML configuration file."""
|
||||
config_file = tmp_path / "pyproject.toml"
|
||||
config_file.write_text("""
|
||||
[tool.auto-readme]
|
||||
filename = "README.md"
|
||||
sections = ["title", "description", "overview"]
|
||||
""")
|
||||
|
||||
config = ConfigLoader.load(config_file)
|
||||
|
||||
assert config.output_filename == "README.md"
|
||||
|
||||
def test_load_nonexistent_file(self):
|
||||
"""Test loading nonexistent configuration file."""
|
||||
config = ConfigLoader.load(Path("/nonexistent/config.yaml"))
|
||||
assert config.project_name is None
|
||||
|
||||
def test_load_invalid_yaml(self, tmp_path):
|
||||
"""Test loading invalid YAML raises error."""
|
||||
config_file = tmp_path / ".readmerc.yaml"
|
||||
config_file.write_text("invalid: yaml: content: [")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ConfigLoader.load(config_file)
|
||||
|
||||
|
||||
class TestConfigValidator:
|
||||
"""Tests for ConfigValidator."""
|
||||
|
||||
def test_validate_valid_config(self):
|
||||
"""Test validating a valid configuration."""
|
||||
config = ReadmeConfig(
|
||||
project_name="test",
|
||||
template="base",
|
||||
)
|
||||
|
||||
errors = ConfigValidator.validate(config)
|
||||
assert len(errors) == 0
|
||||
|
||||
def test_validate_invalid_template(self):
|
||||
"""Test validating invalid template name."""
|
||||
config = ReadmeConfig(
|
||||
project_name="test",
|
||||
template="nonexistent",
|
||||
)
|
||||
|
||||
errors = ConfigValidator.validate(config)
|
||||
assert len(errors) == 1
|
||||
assert "Invalid template" in errors[0]
|
||||
|
||||
def test_validate_invalid_section(self):
|
||||
"""Test validating invalid section name."""
|
||||
config = ReadmeConfig(
|
||||
project_name="test",
|
||||
sections={"order": ["invalid_section"]},
|
||||
)
|
||||
|
||||
errors = ConfigValidator.validate(config)
|
||||
assert len(errors) == 1
|
||||
assert "Invalid section" in errors[0]
|
||||
|
||||
def test_generate_template(self):
|
||||
"""Test generating configuration template."""
|
||||
template = ConfigValidator.generate_template()
|
||||
|
||||
assert "project_name:" in template
|
||||
assert "description:" in template
|
||||
assert "template:" in template
|
||||
assert "interactive:" in template
|
||||
242
tests/test_parsers.py
Normal file
242
tests/test_parsers.py
Normal file
@@ -0,0 +1,242 @@
|
||||
"""Tests for dependency parsers."""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
from src.auto_readme.parsers import (
|
||||
PythonDependencyParser,
|
||||
JavaScriptDependencyParser,
|
||||
GoDependencyParser,
|
||||
RustDependencyParser,
|
||||
DependencyParserFactory,
|
||||
)
|
||||
|
||||
|
||||
class TestPythonDependencyParser:
|
||||
"""Tests for PythonDependencyParser."""
|
||||
|
||||
def test_can_parse_requirements_txt(self):
|
||||
"""Test that parser recognizes requirements.txt."""
|
||||
parser = PythonDependencyParser()
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
req_file = Path(tmp_dir) / "requirements.txt"
|
||||
req_file.write_text("requests>=2.31.0\n")
|
||||
assert parser.can_parse(req_file)
|
||||
|
||||
def test_can_parse_pyproject_toml(self):
|
||||
"""Test that parser recognizes pyproject.toml."""
|
||||
parser = PythonDependencyParser()
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
pyproject_file = Path(tmp_dir) / "pyproject.toml"
|
||||
pyproject_file.write_text('[project]\ndependencies = ["requests>=2.0"]')
|
||||
assert parser.can_parse(pyproject_file)
|
||||
|
||||
def test_parse_requirements_txt(self):
|
||||
"""Test parsing requirements.txt file."""
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
req_file = Path(tmp_dir) / "requirements.txt"
|
||||
req_file.write_text("""
|
||||
requests>=2.31.0
|
||||
click>=8.0.0
|
||||
pytest==7.0.0
|
||||
-e git+https://github.com/user/repo.git#egg=package
|
||||
# This is a comment
|
||||
numpy~=1.24.0
|
||||
""")
|
||||
|
||||
parser = PythonDependencyParser()
|
||||
deps = parser.parse(req_file)
|
||||
|
||||
assert len(deps) == 5
|
||||
names = {d.name for d in deps}
|
||||
assert "requests" in names
|
||||
assert "click" in names
|
||||
assert "pytest" in names
|
||||
assert "numpy" in names
|
||||
|
||||
def test_parse_pyproject_toml(self):
|
||||
"""Test parsing pyproject.toml file."""
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
pyproject_file = Path(tmp_dir) / "pyproject.toml"
|
||||
pyproject_file.write_text("""
|
||||
[project]
|
||||
name = "test-project"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"requests>=2.31.0",
|
||||
"click>=8.0.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["pytest>=7.0.0", "black>=23.0.0"]
|
||||
""")
|
||||
|
||||
parser = PythonDependencyParser()
|
||||
deps = parser.parse(pyproject_file)
|
||||
|
||||
assert len(deps) == 4
|
||||
|
||||
|
||||
class TestJavaScriptDependencyParser:
|
||||
"""Tests for JavaScriptDependencyParser."""
|
||||
|
||||
def test_can_parse_package_json(self):
|
||||
"""Test that parser recognizes package.json."""
|
||||
parser = JavaScriptDependencyParser()
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
package_file = Path(tmp_dir) / "package.json"
|
||||
package_file.write_text('{"name": "test"}')
|
||||
assert parser.can_parse(package_file)
|
||||
|
||||
def test_parse_package_json(self):
|
||||
"""Test parsing package.json file."""
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
package_file = Path(tmp_dir) / "package.json"
|
||||
package_file.write_text("""
|
||||
{
|
||||
"name": "test-project",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"express": "^4.18.0",
|
||||
"lodash": "~4.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"bcrypt": "^5.0.0"
|
||||
}
|
||||
}
|
||||
""")
|
||||
|
||||
parser = JavaScriptDependencyParser()
|
||||
deps = parser.parse(package_file)
|
||||
|
||||
assert len(deps) == 4
|
||||
|
||||
express = next((d for d in deps if d.name == "express"), None)
|
||||
assert express is not None
|
||||
assert express.version == "4.18.0"
|
||||
assert not express.is_dev
|
||||
|
||||
jest = next((d for d in deps if d.name == "jest"), None)
|
||||
assert jest is not None
|
||||
assert jest.is_dev
|
||||
|
||||
bcrypt = next((d for d in deps if d.name == "bcrypt"), None)
|
||||
assert bcrypt is not None
|
||||
assert bcrypt.is_optional
|
||||
|
||||
|
||||
class TestGoDependencyParser:
|
||||
"""Tests for GoDependencyParser."""
|
||||
|
||||
def test_can_parse_go_mod(self):
|
||||
"""Test that parser recognizes go.mod."""
|
||||
parser = GoDependencyParser()
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
go_mod_file = Path(tmp_dir) / "go.mod"
|
||||
go_mod_file.write_text("module test\n")
|
||||
assert parser.can_parse(go_mod_file)
|
||||
|
||||
def test_parse_go_mod(self):
|
||||
"""Test parsing go.mod file."""
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
go_mod_file = Path(tmp_dir) / "go.mod"
|
||||
go_mod_file.write_text("""
|
||||
module test-go-project
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
)
|
||||
|
||||
require github.com/user/repo v1.0.0
|
||||
""")
|
||||
|
||||
parser = GoDependencyParser()
|
||||
deps = parser.parse(go_mod_file)
|
||||
|
||||
assert len(deps) == 3
|
||||
|
||||
gin = next((d for d in deps if d.name == "github.com/gin-gonic/gin"), None)
|
||||
assert gin is not None
|
||||
assert gin.version is not None and "1.9.0" in gin.version
|
||||
|
||||
|
||||
class TestRustDependencyParser:
|
||||
"""Tests for RustDependencyParser."""
|
||||
|
||||
def test_can_parse_cargo_toml(self):
|
||||
"""Test that parser recognizes Cargo.toml."""
|
||||
parser = RustDependencyParser()
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
cargo_file = Path(tmp_dir) / "Cargo.toml"
|
||||
cargo_file.write_text('[package]\nname = "test"')
|
||||
assert parser.can_parse(cargo_file)
|
||||
|
||||
def test_parse_cargo_toml(self):
|
||||
"""Test parsing Cargo.toml file."""
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
cargo_file = Path(tmp_dir) / "Cargo.toml"
|
||||
cargo_file.write_text("""
|
||||
[package]
|
||||
name = "test-project"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
assertions = "0.1"
|
||||
""")
|
||||
|
||||
parser = RustDependencyParser()
|
||||
deps = parser.parse(cargo_file)
|
||||
|
||||
assert len(deps) == 3
|
||||
|
||||
serde = next((d for d in deps if d.name == "serde"), None)
|
||||
assert serde is not None
|
||||
assert serde.version is not None and "1.0" in serde.version
|
||||
assert not serde.is_dev
|
||||
|
||||
assertions = next((d for d in deps if d.name == "assertions"), None)
|
||||
assert assertions is not None
|
||||
assert assertions.is_dev
|
||||
|
||||
|
||||
class TestDependencyParserFactory:
|
||||
"""Tests for DependencyParserFactory."""
|
||||
|
||||
def test_get_parser_python(self):
|
||||
"""Test getting parser for Python file."""
|
||||
parser = DependencyParserFactory.get_parser(Path("requirements.txt"))
|
||||
assert isinstance(parser, PythonDependencyParser)
|
||||
|
||||
def test_get_parser_js(self):
|
||||
"""Test getting parser for JavaScript file."""
|
||||
parser = DependencyParserFactory.get_parser(Path("package.json"))
|
||||
assert isinstance(parser, JavaScriptDependencyParser)
|
||||
|
||||
def test_get_parser_go(self):
|
||||
"""Test getting parser for Go file."""
|
||||
parser = DependencyParserFactory.get_parser(Path("go.mod"))
|
||||
assert isinstance(parser, GoDependencyParser)
|
||||
|
||||
def test_get_parser_rust(self):
|
||||
"""Test getting parser for Rust file."""
|
||||
parser = DependencyParserFactory.get_parser(Path("Cargo.toml"))
|
||||
assert isinstance(parser, RustDependencyParser)
|
||||
|
||||
def test_can_parse(self):
|
||||
"""Test can_parse returns correct results."""
|
||||
assert DependencyParserFactory.can_parse(Path("requirements.txt"))
|
||||
assert DependencyParserFactory.can_parse(Path("package.json"))
|
||||
assert DependencyParserFactory.can_parse(Path("go.mod"))
|
||||
assert DependencyParserFactory.can_parse(Path("Cargo.toml"))
|
||||
assert not DependencyParserFactory.can_parse(Path("random.txt"))
|
||||
197
tests/test_templates.py
Normal file
197
tests/test_templates.py
Normal file
@@ -0,0 +1,197 @@
|
||||
"""Tests for template rendering."""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
from src.auto_readme.models import Project, ProjectConfig, ProjectType
|
||||
from src.auto_readme.templates import TemplateRenderer, TemplateManager
|
||||
|
||||
|
||||
class TestTemplateRenderer:
|
||||
"""Tests for TemplateRenderer."""
|
||||
|
||||
def test_render_base_template(self):
|
||||
"""Test rendering the base template."""
|
||||
project = Project(
|
||||
root_path=Path("/test"),
|
||||
project_type=ProjectType.PYTHON,
|
||||
config=ProjectConfig(
|
||||
name="test-project",
|
||||
description="A test project",
|
||||
),
|
||||
)
|
||||
|
||||
renderer = TemplateRenderer()
|
||||
content = renderer.render(project, "base")
|
||||
|
||||
assert "# test-project" in content
|
||||
assert "A test project" in content
|
||||
assert "## Overview" in content
|
||||
|
||||
def test_render_minimal_template(self):
|
||||
"""Test rendering the minimal template."""
|
||||
project = Project(
|
||||
root_path=Path("/test"),
|
||||
project_type=ProjectType.PYTHON,
|
||||
)
|
||||
|
||||
renderer = TemplateRenderer()
|
||||
content = renderer.render(project, "base")
|
||||
|
||||
assert "Generated by Auto README Generator" in content
|
||||
|
||||
def test_tech_stack_detection(self):
|
||||
"""Test technology stack detection."""
|
||||
from src.auto_readme.models import Dependency
|
||||
|
||||
project = Project(
|
||||
root_path=Path("/test"),
|
||||
project_type=ProjectType.PYTHON,
|
||||
dependencies=[
|
||||
Dependency(name="fastapi"),
|
||||
Dependency(name="flask"),
|
||||
Dependency(name="requests"),
|
||||
],
|
||||
)
|
||||
|
||||
renderer = TemplateRenderer()
|
||||
context = renderer._build_context(project)
|
||||
|
||||
assert "FastAPI" in context["tech_stack"]
|
||||
assert "Flask" in context["tech_stack"]
|
||||
|
||||
def test_installation_steps_generation(self):
|
||||
"""Test automatic installation steps generation."""
|
||||
project = Project(
|
||||
root_path=Path("/test"),
|
||||
project_type=ProjectType.PYTHON,
|
||||
)
|
||||
|
||||
renderer = TemplateRenderer()
|
||||
context = renderer._build_context(project)
|
||||
|
||||
assert "pip install" in context["installation_steps"][0]
|
||||
|
||||
def test_go_installation_steps(self):
|
||||
"""Test Go installation steps."""
|
||||
project = Project(
|
||||
root_path=Path("/test"),
|
||||
project_type=ProjectType.GO,
|
||||
)
|
||||
|
||||
renderer = TemplateRenderer()
|
||||
context = renderer._build_context(project)
|
||||
|
||||
assert "go mod download" in context["installation_steps"][0]
|
||||
|
||||
def test_rust_installation_steps(self):
|
||||
"""Test Rust installation steps."""
|
||||
project = Project(
|
||||
root_path=Path("/test"),
|
||||
project_type=ProjectType.RUST,
|
||||
)
|
||||
|
||||
renderer = TemplateRenderer()
|
||||
context = renderer._build_context(project)
|
||||
|
||||
assert "cargo build" in context["installation_steps"][0]
|
||||
|
||||
def test_feature_detection(self):
|
||||
"""Test automatic feature detection."""
|
||||
from src.auto_readme.models import Function, Class, SourceFile, FileType
|
||||
|
||||
functions = [
|
||||
Function(name="test_func", line_number=1),
|
||||
]
|
||||
classes = [
|
||||
Class(name="TestClass", line_number=1),
|
||||
]
|
||||
|
||||
project = Project(
|
||||
root_path=Path("/test"),
|
||||
project_type=ProjectType.PYTHON,
|
||||
files=[
|
||||
SourceFile(
|
||||
path=Path("test.py"),
|
||||
file_type=FileType.SOURCE,
|
||||
functions=functions,
|
||||
classes=classes,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
renderer = TemplateRenderer()
|
||||
context = renderer._build_context(project)
|
||||
|
||||
assert "Contains 1 classes" in context["features"]
|
||||
assert "Contains 1 functions" in context["features"]
|
||||
|
||||
def test_custom_context_override(self):
|
||||
"""Test custom context can override auto-detected values."""
|
||||
project = Project(
|
||||
root_path=Path("/test"),
|
||||
project_type=ProjectType.PYTHON,
|
||||
)
|
||||
|
||||
renderer = TemplateRenderer()
|
||||
content = renderer.render(
|
||||
project,
|
||||
"base",
|
||||
title="Custom Title",
|
||||
description="Custom description",
|
||||
)
|
||||
|
||||
assert "# Custom Title" in content
|
||||
assert "Custom description" in content
|
||||
|
||||
def test_contributing_guidelines(self):
|
||||
"""Test contributing guidelines generation."""
|
||||
project = Project(
|
||||
root_path=Path("/test"),
|
||||
project_type=ProjectType.PYTHON,
|
||||
)
|
||||
|
||||
renderer = TemplateRenderer()
|
||||
context = renderer._build_context(project)
|
||||
|
||||
assert "Fork the repository" in context["contributing_guidelines"]
|
||||
assert "git checkout -b" in context["contributing_guidelines"]
|
||||
assert "pytest" in context["contributing_guidelines"]
|
||||
|
||||
def test_javascript_contributing_guidelines(self):
|
||||
"""Test JavaScript contributing guidelines."""
|
||||
project = Project(
|
||||
root_path=Path("/test"),
|
||||
project_type=ProjectType.JAVASCRIPT,
|
||||
)
|
||||
|
||||
renderer = TemplateRenderer()
|
||||
context = renderer._build_context(project)
|
||||
|
||||
assert "npm test" in context["contributing_guidelines"]
|
||||
|
||||
|
||||
class TestTemplateManager:
|
||||
"""Tests for TemplateManager."""
|
||||
|
||||
def test_list_templates(self):
|
||||
"""Test listing available templates."""
|
||||
manager = TemplateManager()
|
||||
templates = manager.list_templates()
|
||||
|
||||
assert "base" in templates
|
||||
|
||||
def test_get_template_path_builtin(self):
|
||||
"""Test getting path for built-in template."""
|
||||
manager = TemplateManager()
|
||||
path = manager.get_template_path("base")
|
||||
|
||||
assert path is None
|
||||
|
||||
def test_get_template_path_custom(self):
|
||||
"""Test getting path for custom template."""
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
manager = TemplateManager(Path(tmp_dir))
|
||||
path = manager.get_template_path("custom.md.j2")
|
||||
assert path is not None
|
||||
132
tests/test_utils.py
Normal file
132
tests/test_utils.py
Normal file
@@ -0,0 +1,132 @@
|
||||
"""Tests for utility modules."""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
from src.auto_readme.utils.path_utils import PathUtils
|
||||
from src.auto_readme.utils.file_scanner import FileScanner
|
||||
|
||||
|
||||
class TestPathUtils:
|
||||
"""Tests for PathUtils."""
|
||||
|
||||
def test_normalize_path(self):
|
||||
"""Test path normalization."""
|
||||
path = PathUtils.normalize_path("./foo/bar")
|
||||
assert path.is_absolute()
|
||||
|
||||
def test_is_ignored_dir(self):
|
||||
"""Test ignored directory detection."""
|
||||
assert PathUtils.is_ignored_dir(Path("__pycache__"))
|
||||
assert PathUtils.is_ignored_dir(Path(".git"))
|
||||
assert PathUtils.is_ignored_dir(Path("node_modules"))
|
||||
assert not PathUtils.is_ignored_dir(Path("src"))
|
||||
|
||||
def test_is_ignored_file(self):
|
||||
"""Test ignored file detection."""
|
||||
assert PathUtils.is_ignored_file(Path(".gitignore"))
|
||||
assert not PathUtils.is_ignored_file(Path("main.py"))
|
||||
|
||||
def test_is_source_file(self):
|
||||
"""Test source file detection."""
|
||||
assert PathUtils.is_source_file(Path("main.py"))
|
||||
assert PathUtils.is_source_file(Path("index.js"))
|
||||
assert PathUtils.is_source_file(Path("main.go"))
|
||||
assert PathUtils.is_source_file(Path("lib.rs"))
|
||||
assert not PathUtils.is_source_file(Path("README.md"))
|
||||
|
||||
def test_is_config_file(self):
|
||||
"""Test config file detection."""
|
||||
assert PathUtils.is_config_file(Path("config.json"))
|
||||
assert PathUtils.is_config_file(Path("settings.yaml"))
|
||||
assert PathUtils.is_config_file(Path("pyproject.toml"))
|
||||
|
||||
def test_is_test_file(self):
|
||||
"""Test test file detection."""
|
||||
assert PathUtils.is_test_file(Path("test_main.py"))
|
||||
assert PathUtils.is_test_file(Path("tests/utils.py"))
|
||||
assert PathUtils.is_test_file(Path("example.test.js"))
|
||||
assert not PathUtils.is_test_file(Path("main.py"))
|
||||
|
||||
def test_is_hidden(self):
|
||||
"""Test hidden file detection."""
|
||||
assert PathUtils.is_hidden(Path(".gitignore"))
|
||||
assert not PathUtils.is_hidden(Path("main.py"))
|
||||
|
||||
def test_detect_project_root(self):
|
||||
"""Test project root detection."""
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
root = Path(tmp_dir)
|
||||
subdir = root / "src" / "package"
|
||||
subdir.mkdir(parents=True)
|
||||
|
||||
(root / "pyproject.toml").touch()
|
||||
|
||||
detected = PathUtils.detect_project_root(subdir)
|
||||
assert detected == root
|
||||
|
||||
def test_get_relative_path(self):
|
||||
"""Test relative path calculation."""
|
||||
base = Path("/home/user/project")
|
||||
target = Path("/home/user/project/src/main.py")
|
||||
relative = PathUtils.get_relative_path(target, base)
|
||||
assert relative == Path("src/main.py")
|
||||
|
||||
|
||||
class TestFileScanner:
|
||||
"""Tests for FileScanner."""
|
||||
|
||||
def test_scan_python_project(self, create_python_project):
|
||||
"""Test scanning a Python project."""
|
||||
scanner = FileScanner(create_python_project)
|
||||
files = scanner.scan_and_create()
|
||||
|
||||
file_names = {f.path.name for f in files}
|
||||
assert "main.py" in file_names
|
||||
assert "__init__.py" in file_names
|
||||
assert "requirements.txt" in file_names
|
||||
assert "pyproject.toml" in file_names
|
||||
|
||||
def test_detect_project_type_python(self, create_python_project):
|
||||
"""Test project type detection for Python."""
|
||||
from src.auto_readme.models import ProjectType
|
||||
|
||||
scanner = FileScanner(create_python_project)
|
||||
project_type = scanner.detect_project_type()
|
||||
|
||||
assert project_type == ProjectType.PYTHON
|
||||
|
||||
def test_detect_project_type_javascript(self, create_javascript_project):
|
||||
"""Test project type detection for JavaScript."""
|
||||
from src.auto_readme.models import ProjectType
|
||||
|
||||
scanner = FileScanner(create_javascript_project)
|
||||
project_type = scanner.detect_project_type()
|
||||
|
||||
assert project_type == ProjectType.JAVASCRIPT
|
||||
|
||||
def test_detect_project_type_go(self, create_go_project):
|
||||
"""Test project type detection for Go."""
|
||||
from src.auto_readme.models import ProjectType
|
||||
|
||||
scanner = FileScanner(create_go_project)
|
||||
project_type = scanner.detect_project_type()
|
||||
|
||||
assert project_type == ProjectType.GO
|
||||
|
||||
def test_detect_project_type_rust(self, create_rust_project):
|
||||
"""Test project type detection for Rust."""
|
||||
from src.auto_readme.models import ProjectType
|
||||
|
||||
scanner = FileScanner(create_rust_project)
|
||||
project_type = scanner.detect_project_type()
|
||||
|
||||
assert project_type == ProjectType.RUST
|
||||
|
||||
def test_scan_mixed_project(self, create_mixed_project):
|
||||
"""Test scanning a project with multiple languages."""
|
||||
scanner = FileScanner(create_mixed_project)
|
||||
files = scanner.scan_and_create()
|
||||
|
||||
assert len(files) > 0
|
||||
Reference in New Issue
Block a user