diff --git a/tests/test_parsers.py b/tests/test_parsers.py new file mode 100644 index 0000000..1a66ced --- /dev/null +++ b/tests/test_parsers.py @@ -0,0 +1,241 @@ +"""Tests for dependency parsers.""" + +import tempfile +from pathlib import Path + +from src.auto_readme.parsers import ( + DependencyParserFactory, + GoDependencyParser, + JavaScriptDependencyParser, + PythonDependencyParser, + RustDependencyParser, +) + + +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"))