diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..0c7e54c
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,32 @@
+name: Test Suite
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install .
+ pip install pytest
+
+ - name: Run tests
+ run: |
+ pytest --tb=short -v
diff --git a/pyproject.toml b/pyproject.toml
index 8a0e4aa..bdd6c47 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,3 +20,7 @@ codemappr = "codemappr.cli:app"
[tool.setuptools.packages.find]
where = ["."]
include = ["codemappr*"]
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+pythonpath = ["."]
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..7cf272c
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,105 @@
+import pytest
+import os
+
+@pytest.fixture
+def mock_react_project(tmp_path):
+ project_dir = tmp_path / "mock_react_project"
+ project_dir.mkdir()
+
+ package_json = project_dir / "package.json"
+ package_json.write_text('{"dependencies": {"react": "^18.2.0"}}')
+
+ src_dir = project_dir / "src"
+ src_dir.mkdir()
+ (src_dir / "App.js").write_text("function App() { return
Hello
; }")
+
+ public_dir = project_dir / "public"
+ public_dir.mkdir()
+ (public_dir / "index.html").write_text("")
+
+ return project_dir
+
+@pytest.fixture
+def mock_django_project(tmp_path):
+ project_dir = tmp_path / "mock_django_project"
+ project_dir.mkdir()
+
+ (project_dir / "manage.py").write_text("# Django manage.py")
+
+ app_dir = project_dir / "myproject"
+ app_dir.mkdir()
+ (app_dir / "settings.py").write_text("DEBUG = True")
+ (app_dir / "urls.py").write_text("urlpatterns = []")
+
+ (project_dir / "requirements.txt").write_text("django==4.2")
+
+ return project_dir
+
+@pytest.fixture
+def mock_rust_project(tmp_path):
+ project_dir = tmp_path / "mock_rust_project"
+ project_dir.mkdir()
+
+ (project_dir / "Cargo.toml").write_text('[package]\nname = "mock_rust"')
+
+ src_dir = project_dir / "src"
+ src_dir.mkdir()
+ (src_dir / "main.rs").write_text("fn main() {}")
+
+ return project_dir
+
+@pytest.fixture
+def mock_python_package(tmp_path):
+ project_dir = tmp_path / "mock_python_package"
+ project_dir.mkdir()
+
+ (project_dir / "pyproject.toml").write_text("[project]\nname = 'mock_python'")
+
+ src_dir = project_dir / "src"
+ src_dir.mkdir()
+ (src_dir / "__init__.py").write_text("")
+
+ tests_dir = project_dir / "tests"
+ tests_dir.mkdir()
+ (tests_dir / "__init__.py").write_text("")
+
+ return project_dir
+
+@pytest.fixture
+def mock_monorepo(tmp_path):
+ project_dir = tmp_path / "mock_monorepo"
+ project_dir.mkdir()
+
+ (project_dir / "package.json").write_text('{"workspaces": ["packages/*"]}')
+
+ packages_dir = project_dir / "packages"
+ packages_dir.mkdir()
+
+ app1_dir = packages_dir / "app1"
+ app1_dir.mkdir()
+ (app1_dir / "package.json").write_text('{"name": "app1"}')
+
+ app2_dir = packages_dir / "app2"
+ app2_dir.mkdir()
+ (app2_dir / "package.json").write_text('{"name": "app2"}')
+
+ return project_dir
+
+@pytest.fixture
+def mock_empty_dir(tmp_path):
+ project_dir = tmp_path / "mock_empty_dir"
+ project_dir.mkdir()
+ return project_dir
+
+@pytest.fixture
+def mock_deep_nested(tmp_path):
+ project_dir = tmp_path / "mock_deep_nested"
+ project_dir.mkdir()
+
+ current = project_dir
+ for i in range(1, 11):
+ current = current / f"level{i}"
+ current.mkdir()
+ (current / f"file{i}.txt").write_text(f"content{i}")
+
+ return project_dir
diff --git a/tests/test_detector.py b/tests/test_detector.py
index 5ce4bd2..033be34 100644
--- a/tests/test_detector.py
+++ b/tests/test_detector.py
@@ -1,61 +1,47 @@
-import os
-import shutil
-import tempfile
-import pytest
from codemappr.walker import scan_directory
from codemappr.detector import detect_project
-@pytest.fixture
-def temp_dir():
- d = tempfile.mkdtemp()
- yield d
- shutil.rmtree(d)
+def test_detect_react(mock_react_project):
+ node = scan_directory(str(mock_react_project))
+ profile = detect_project(node, str(mock_react_project))
-def test_detect_python_package(temp_dir):
- # Create a dummy Python package structure
- with open(os.path.join(temp_dir, "pyproject.toml"), "w") as f:
- f.write("[project]\nname='test'")
- os.makedirs(os.path.join(temp_dir, "src"))
- with open(os.path.join(temp_dir, "src", "main.py"), "w") as f:
- f.write("print('hello')")
+ assert profile.project_type == "React/Next.js"
+ assert profile.framework == "React"
+ assert "JavaScript" in profile.language_stack
- node = scan_directory(temp_dir)
- profile = detect_project(node, temp_dir)
+def test_detect_django(mock_django_project):
+ node = scan_directory(str(mock_django_project))
+ profile = detect_project(node, str(mock_django_project))
- assert profile.project_type == "Python Package/Library"
+ assert profile.project_type == "Python Django"
+ assert profile.framework == "Django"
assert "Python" in profile.language_stack
- assert profile.confidence in ["medium", "high"]
-def test_detect_react_project(temp_dir):
- # Create a dummy React structure
- with open(os.path.join(temp_dir, "package.json"), "w") as f:
- f.write('{"dependencies": {"react": "18.0.0"}}')
- os.makedirs(os.path.join(temp_dir, "src"))
- with open(os.path.join(temp_dir, "src", "App.js"), "w") as f:
- f.write("function App() {}")
+def test_detect_rust(mock_rust_project):
+ node = scan_directory(str(mock_rust_project))
+ profile = detect_project(node, str(mock_rust_project))
- node = scan_directory(temp_dir)
- profile = detect_project(node, temp_dir)
+ assert profile.project_type == "Rust"
+ assert "Rust" in profile.language_stack
- assert profile.project_type == "React/Next.js"
- assert profile.framework == "React"
- assert "JavaScript" in profile.language_stack
- assert profile.confidence == "high"
+def test_detect_monorepo(mock_monorepo):
+ node = scan_directory(str(mock_monorepo))
+ profile = detect_project(node, str(mock_monorepo))
-def test_detect_django_project(temp_dir):
- # Create dummy Django structure
- with open(os.path.join(temp_dir, "manage.py"), "w") as f:
- f.write("# django manage.py")
- os.makedirs(os.path.join(temp_dir, "myproject"))
- with open(os.path.join(temp_dir, "myproject", "settings.py"), "w") as f:
- f.write("DEBUG=True")
- with open(os.path.join(temp_dir, "myproject", "urls.py"), "w") as f:
- f.write("urlpatterns=[]")
+ assert profile.project_type == "Monorepo"
- node = scan_directory(temp_dir)
- profile = detect_project(node, temp_dir)
+def test_detect_unknown(mock_empty_dir):
+ node = scan_directory(str(mock_empty_dir))
+ profile = detect_project(node, str(mock_empty_dir))
- assert profile.project_type == "Python Django"
- assert profile.framework == "Django"
+ assert profile.project_type == "Unknown"
+
+def test_language_stack(mock_react_project):
+ # Add a python file to check multi-language
+ (mock_react_project / "script.py").write_text("print(1)")
+
+ node = scan_directory(str(mock_react_project))
+ profile = detect_project(node, str(mock_react_project))
+
+ assert "JavaScript" in profile.language_stack
assert "Python" in profile.language_stack
- assert profile.confidence == "high"
diff --git a/tests/test_explainer.py b/tests/test_explainer.py
index 1a56b94..f1bd421 100644
--- a/tests/test_explainer.py
+++ b/tests/test_explainer.py
@@ -1,58 +1,34 @@
-from codemappr.models import DirectoryNode, ProjectProfile
+from codemappr.walker import scan_directory
+from codemappr.detector import detect_project
from codemappr.explainer import explain_project
-def test_explain_project_basic():
- profile = ProjectProfile(
- project_type="Python Django",
- language_stack=["Python"],
- description="A Python Django project.",
- root_path="/tmp/test",
- total_files=10,
- total_dirs=2,
- total_size_bytes=1024,
- confidence="high",
- framework="Django"
- )
-
- node = DirectoryNode(
- name="test",
- path=".",
- is_dir=True,
- size=1024,
- extension="",
- depth=0,
- children=[
- DirectoryNode("manage.py", "./manage.py", False, 100, ".py", 1),
- DirectoryNode("src", "./src", True, 924, "", 1),
- DirectoryNode("README.md", "./README.md", False, 50, ".md", 1)
- ]
- )
+def test_summary_not_empty(mock_react_project):
+ node = scan_directory(str(mock_react_project))
+ profile = detect_project(node, str(mock_react_project))
+ explanation = explain_project(profile, node)
+
+ assert explanation["summary"]
+ assert isinstance(explanation["summary"], str)
+def test_folder_purposes(mock_python_package):
+ node = scan_directory(str(mock_python_package))
+ profile = detect_project(node, str(mock_python_package))
explanation = explain_project(profile, node)
- assert "Python Django" in explanation["summary"]
- assert "Django" in explanation["summary"]
+ # Check if 'src' and 'tests' are recognized
assert explanation["structure"]["src"] == "Main source code"
+ assert explanation["structure"]["tests"] == "Test suite"
+
+def test_entry_points_detected(mock_django_project):
+ node = scan_directory(str(mock_django_project))
+ profile = detect_project(node, str(mock_django_project))
+ explanation = explain_project(profile, node)
+
assert "manage.py" in explanation["entry_points"]
- assert "README.md" in explanation["notable_files"]
-
-def test_explain_project_empty():
- profile = ProjectProfile(
- project_type="Unknown",
- language_stack=[],
- description="A Unknown project.",
- root_path="/tmp/test",
- total_files=0,
- total_dirs=1,
- total_size_bytes=0,
- confidence="low"
- )
-
- node = DirectoryNode("test", ".", True, 0, "", 0)
+def test_notable_files_detected(mock_react_project):
+ node = scan_directory(str(mock_react_project))
+ profile = detect_project(node, str(mock_react_project))
explanation = explain_project(profile, node)
- assert "Unknown" in explanation["summary"]
- assert explanation["structure"] == {}
- assert explanation["entry_points"] == []
- assert explanation["notable_files"] == []
+ assert "package.json" in explanation["notable_files"]
diff --git a/tests/test_exporters.py b/tests/test_exporters.py
new file mode 100644
index 0000000..4497dd9
--- /dev/null
+++ b/tests/test_exporters.py
@@ -0,0 +1,49 @@
+import os
+from codemappr.walker import scan_directory
+from codemappr.detector import detect_project
+from codemappr.explainer import explain_project
+from codemappr.exporter import export_markdown, export_html
+
+def test_markdown_export(mock_react_project, tmp_path):
+ node = scan_directory(str(mock_react_project))
+ profile = detect_project(node, str(mock_react_project))
+ explanation = explain_project(profile, node)
+
+ output_path = tmp_path / "REPORT.md"
+ export_markdown(node, profile, explanation, str(output_path))
+
+ assert output_path.exists()
+ content = output_path.read_text()
+ assert "# CodeMappr Report" in content
+ assert "## Project Profile" in content
+ assert "## Directory Tree" in content
+ assert "## Architecture Summary" in content
+
+def test_html_export(mock_react_project, tmp_path):
+ node = scan_directory(str(mock_react_project))
+ profile = detect_project(node, str(mock_react_project))
+ explanation = explain_project(profile, node)
+
+ output_path = tmp_path / "REPORT.html"
+ export_html(node, profile, explanation, str(output_path))
+
+ assert output_path.exists()
+ content = output_path.read_text()
+ assert "