fix: resolve CI test failure in output.py
- Fixed undefined 'tool' variable in display_history function - Changed '[tool]' markup tag usage to proper Rich syntax - All tests now pass (38/38 unit tests) - Type checking passes with mypy --strict
This commit is contained in:
395
tests/test_parsers.py
Normal file
395
tests/test_parsers.py
Normal file
@@ -0,0 +1,395 @@
|
||||
"""Tests for parsers module."""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from doc2man.parsers.python import PythonDocstringParser, parse_python_file
|
||||
from doc2man.parsers.go import GoDocstringParser, parse_go_file
|
||||
from doc2man.parsers.javascript import JavaScriptDocstringParser, parse_javascript_file
|
||||
|
||||
|
||||
class TestPythonDocstringParser:
|
||||
"""Tests for Python docstring parser."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test fixtures."""
|
||||
self.parser = PythonDocstringParser()
|
||||
|
||||
def test_parse_simple_function(self):
|
||||
"""Test parsing a simple function with docstring."""
|
||||
source = '''
|
||||
def hello(name):
|
||||
"""Say hello to a person.
|
||||
|
||||
Args:
|
||||
name: The name of the person to greet.
|
||||
|
||||
Returns:
|
||||
A greeting message.
|
||||
"""
|
||||
return f"Hello, {name}!"
|
||||
'''
|
||||
result = self.parser.parse(source)
|
||||
|
||||
assert len(result["functions"]) == 1
|
||||
func = result["functions"][0]
|
||||
assert func["name"] == "hello"
|
||||
assert "greet" in func["description"].lower() or "hello" in func["description"].lower()
|
||||
assert len(func["args"]) >= 1
|
||||
arg_names = [a["name"] for a in func["args"]]
|
||||
assert "name" in arg_names
|
||||
assert func["returns"] is not None
|
||||
|
||||
def test_parse_function_with_google_style(self):
|
||||
"""Test parsing Google-style docstrings."""
|
||||
source = '''
|
||||
def process_data(items, callback=None):
|
||||
"""Process a list of items with optional callback.
|
||||
|
||||
Args:
|
||||
items: List of items to process.
|
||||
callback: Optional function to call for each item.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary with processed results.
|
||||
|
||||
Raises:
|
||||
ValueError: If items is empty.
|
||||
"""
|
||||
if not items:
|
||||
raise ValueError("Items cannot be empty")
|
||||
return {"count": len(items)}
|
||||
'''
|
||||
result = self.parser.parse(source)
|
||||
|
||||
assert len(result["functions"]) == 1
|
||||
func = result["functions"][0]
|
||||
assert len(func["args"]) >= 2
|
||||
arg_names = [a["name"] for a in func["args"]]
|
||||
assert "items" in arg_names
|
||||
assert "callback" in arg_names
|
||||
assert len(func["raises"]) >= 1
|
||||
raises_names = [r["exception"] for r in func["raises"]]
|
||||
assert "ValueError" in raises_names
|
||||
|
||||
def test_parse_function_with_numpy_style(self):
|
||||
"""Test parsing NumPy-style docstrings."""
|
||||
source = '''
|
||||
def calculate_stats(data):
|
||||
"""Calculate statistics for the given data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : array_like
|
||||
Input data array.
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple
|
||||
A tuple containing (mean, std, median).
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> calculate_stats([1, 2, 3, 4, 5])
|
||||
(3.0, 1.414, 3)
|
||||
"""
|
||||
import numpy as np
|
||||
arr = np.array(data)
|
||||
return arr.mean(), arr.std(), np.median(arr)
|
||||
'''
|
||||
result = self.parser.parse(source)
|
||||
|
||||
func = result["functions"][0]
|
||||
arg_names = [a["name"] for a in func["args"]]
|
||||
assert "data" in arg_names
|
||||
assert len(func["examples"]) > 0
|
||||
|
||||
def test_parse_module_docstring(self):
|
||||
"""Test parsing module-level docstring."""
|
||||
source = '''"""This is a module docstring.
|
||||
|
||||
This module provides utility functions for data processing.
|
||||
"""
|
||||
|
||||
def helper():
|
||||
"""A helper function."""
|
||||
pass
|
||||
'''
|
||||
result = self.parser.parse(source)
|
||||
|
||||
assert "This is a module docstring" in result["module_docstring"]
|
||||
|
||||
def test_parse_class(self):
|
||||
"""Test parsing a class with methods."""
|
||||
source = '''
|
||||
class Calculator:
|
||||
"""A simple calculator class.
|
||||
|
||||
Attributes:
|
||||
memory: Current memory value.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the calculator."""
|
||||
self.memory = 0
|
||||
|
||||
def add(self, a, b):
|
||||
"""Add two numbers.
|
||||
|
||||
Args:
|
||||
a: First number.
|
||||
b: Second number.
|
||||
|
||||
Returns:
|
||||
Sum of a and b.
|
||||
"""
|
||||
return a + b
|
||||
'''
|
||||
result = self.parser.parse(source)
|
||||
|
||||
assert len(result["classes"]) == 1
|
||||
cls = result["classes"][0]
|
||||
assert cls["name"] == "Calculator"
|
||||
assert len(cls["methods"]) == 1
|
||||
assert cls["methods"][0]["name"] == "add"
|
||||
|
||||
def test_parse_file(self):
|
||||
"""Test parsing a Python file."""
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
|
||||
f.write('''
|
||||
def example():
|
||||
"""An example function.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
pass
|
||||
''')
|
||||
f.flush()
|
||||
|
||||
result = parse_python_file(Path(f.name))
|
||||
|
||||
assert result["language"] == "python"
|
||||
assert len(result["functions"]) == 1
|
||||
|
||||
Path(f.name).unlink()
|
||||
|
||||
|
||||
class TestGoDocstringParser:
|
||||
"""Tests for Go docstring parser."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test fixtures."""
|
||||
self.parser = GoDocstringParser()
|
||||
|
||||
def test_parse_simple_function(self):
|
||||
"""Test parsing a simple Go function."""
|
||||
source = '''// Add adds two integers and returns the result.
|
||||
//
|
||||
// Parameters:
|
||||
// a: First integer
|
||||
// b: Second integer
|
||||
//
|
||||
// Returns: The sum of a and b
|
||||
func Add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
'''
|
||||
result = self.parser.parse_content(source)
|
||||
|
||||
assert len(result["functions"]) == 1
|
||||
func = result["functions"][0]
|
||||
assert func["name"] == "Add"
|
||||
|
||||
def test_parse_function_with_params(self):
|
||||
"""Test parsing Go function with parameters."""
|
||||
source = '''// Greet returns a greeting message.
|
||||
//
|
||||
// name: The name to greet
|
||||
//
|
||||
// Returns: A greeting string
|
||||
func Greet(name string) string {
|
||||
return "Hello, " + name
|
||||
}
|
||||
'''
|
||||
result = self.parser.parse_content(source)
|
||||
|
||||
func = result["functions"][0]
|
||||
assert func["name"] == "Greet"
|
||||
assert len(func["args"]) >= 1
|
||||
arg_names = [a["name"] for a in func["args"]]
|
||||
assert "name" in arg_names
|
||||
|
||||
def test_parse_package_documentation(self):
|
||||
"""Test parsing package-level documentation."""
|
||||
source = '''// Package math provides mathematical utilities.
|
||||
//
|
||||
// This package contains functions for basic
|
||||
// mathematical operations.
|
||||
package math
|
||||
|
||||
// Add adds two numbers.
|
||||
func Add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
'''
|
||||
result = self.parser.parse_content(source)
|
||||
|
||||
assert "mathematical utilities" in result["package_docstring"].lower()
|
||||
|
||||
def test_parse_file(self):
|
||||
"""Test parsing a Go file."""
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".go", delete=False) as f:
|
||||
f.write('''// Hello returns a greeting.
|
||||
//
|
||||
// Returns: A greeting message
|
||||
func Hello() string {
|
||||
return "Hello, World!"
|
||||
}
|
||||
''')
|
||||
f.flush()
|
||||
|
||||
result = parse_go_file(Path(f.name))
|
||||
|
||||
assert result["language"] == "go"
|
||||
assert len(result["functions"]) == 1
|
||||
|
||||
Path(f.name).unlink()
|
||||
|
||||
|
||||
class TestJavaScriptDocstringParser:
|
||||
"""Tests for JavaScript docstring parser."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test fixtures."""
|
||||
self.parser = JavaScriptDocstringParser()
|
||||
|
||||
def test_parse_simple_function(self):
|
||||
"""Test parsing a simple JavaScript function."""
|
||||
source = '''
|
||||
/**
|
||||
* Says hello to a person.
|
||||
*
|
||||
* @param {string} name - The name of the person
|
||||
* @returns {string} A greeting message
|
||||
*/
|
||||
function hello(name) {
|
||||
return "Hello, " + name;
|
||||
}
|
||||
'''
|
||||
result = self.parser.parse_content(source)
|
||||
|
||||
assert len(result["functions"]) == 1
|
||||
func = result["functions"][0]
|
||||
assert func["name"] == "hello"
|
||||
assert "hello" in func["description"].lower() or "person" in func["description"].lower()
|
||||
assert len(func["args"]) >= 1
|
||||
arg_names = [a["name"] for a in func["args"]]
|
||||
assert "name" in arg_names
|
||||
|
||||
def test_parse_arrow_function(self):
|
||||
"""Test parsing an arrow function."""
|
||||
source = '''
|
||||
/**
|
||||
* Adds two numbers.
|
||||
*
|
||||
* @param {number} a - First number
|
||||
* @param {number} b - Second number
|
||||
* @returns {number} The sum
|
||||
*/
|
||||
const add = (a, b) => a + b;
|
||||
'''
|
||||
result = self.parser.parse_content(source)
|
||||
|
||||
func = result["functions"][0]
|
||||
assert func["name"] == "add"
|
||||
assert len(func["args"]) >= 1
|
||||
|
||||
def test_parse_function_with_example(self):
|
||||
"""Test parsing function with examples."""
|
||||
source = '''
|
||||
/**
|
||||
* Squares a number.
|
||||
*
|
||||
* @param {number} n - The number to square
|
||||
* @returns {number} The squared number
|
||||
*
|
||||
* @example
|
||||
* square(5)
|
||||
* // returns 25
|
||||
*/
|
||||
function square(n) {
|
||||
return n * n;
|
||||
}
|
||||
'''
|
||||
result = self.parser.parse_content(source)
|
||||
|
||||
func = result["functions"][0]
|
||||
assert len(func["examples"]) > 0
|
||||
|
||||
def test_parse_export_function(self):
|
||||
"""Test parsing exported function."""
|
||||
source = '''
|
||||
/**
|
||||
* A public API function.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
export function publicApi() {
|
||||
console.log("Hello");
|
||||
}
|
||||
'''
|
||||
result = self.parser.parse_content(source)
|
||||
|
||||
assert len(result["functions"]) == 1
|
||||
func = result["functions"][0]
|
||||
assert func["name"] == "publicApi"
|
||||
|
||||
def test_parse_file(self):
|
||||
"""Test parsing a JavaScript file."""
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".js", delete=False) as f:
|
||||
f.write('''
|
||||
/**
|
||||
* Example function.
|
||||
*
|
||||
* @returns {string} A message
|
||||
*/
|
||||
function example() {
|
||||
return "Hello";
|
||||
}
|
||||
''')
|
||||
f.flush()
|
||||
|
||||
result = parse_javascript_file(Path(f.name))
|
||||
|
||||
assert result["language"] == "javascript"
|
||||
assert len(result["functions"]) == 1
|
||||
|
||||
Path(f.name).unlink()
|
||||
|
||||
def test_parse_typescript(self):
|
||||
"""Test parsing a TypeScript file."""
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".ts", delete=False) as f:
|
||||
f.write('''
|
||||
/**
|
||||
* Adds two numbers.
|
||||
*
|
||||
* @param a - First number
|
||||
* @param b - Second number
|
||||
* @returns The sum
|
||||
*/
|
||||
function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
''')
|
||||
f.flush()
|
||||
|
||||
result = parse_javascript_file(Path(f.name))
|
||||
|
||||
assert len(result["functions"]) >= 1
|
||||
func = result["functions"][0]
|
||||
assert func["returns"] is not None
|
||||
|
||||
Path(f.name).unlink()
|
||||
Reference in New Issue
Block a user