Fixed priority linting errors to improve code quality: Critical Fixes: - F821 (2 errors): Fixed undefined name 'original_result' in config_enhancer.py - UP035 (2 errors): Removed deprecated typing.Dict and typing.Type imports - F401 (27 errors): Removed unused imports and added noqa for availability checks - E722 (19 errors): Replaced bare 'except:' with 'except Exception:' Code Quality Improvements: - SIM201 (4 errors): Simplified 'not x == y' to 'x != y' - SIM118 (2 errors): Removed unnecessary .keys() in dict iterations - E741 (4 errors): Renamed ambiguous variable 'l' to 'line' - I001 (1 error): Sorted imports in test_bootstrap_skill.py All modified areas tested and passing: - test_scraper_features.py: 42 passed - test_integration.py: 51 passed - test_architecture_scenarios.py: 11 passed - test_real_world_fastmcp.py: 19 passed (1 skipped) Remaining linting errors: 249 (mostly code style suggestions like ARG002, F841, SIM102) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
940 lines
32 KiB
Python
940 lines
32 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Tests for how_to_guide_builder.py - Build how-to guides from workflow examples
|
|
|
|
Test Coverage:
|
|
- WorkflowAnalyzer (6 tests) - Step extraction and metadata detection
|
|
- WorkflowGrouper (4 tests) - Grouping strategies
|
|
- GuideGenerator (5 tests) - Markdown generation
|
|
- HowToGuideBuilder (5 tests) - Main orchestrator integration
|
|
- End-to-end (1 test) - Full workflow
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
# Add src to path
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
|
|
|
from skill_seekers.cli.guide_enhancer import StepEnhancement
|
|
from skill_seekers.cli.how_to_guide_builder import (
|
|
GuideCollection,
|
|
GuideGenerator,
|
|
HowToGuide,
|
|
HowToGuideBuilder,
|
|
WorkflowAnalyzer,
|
|
WorkflowGrouper,
|
|
WorkflowStep,
|
|
)
|
|
|
|
|
|
class TestWorkflowAnalyzer(unittest.TestCase):
|
|
"""Tests for WorkflowAnalyzer - Extract steps from workflows"""
|
|
|
|
def setUp(self):
|
|
self.analyzer = WorkflowAnalyzer()
|
|
|
|
def test_analyze_python_workflow(self):
|
|
"""Test analysis of Python workflow with multiple steps"""
|
|
workflow = {
|
|
"code": """
|
|
def test_user_creation_workflow():
|
|
# Step 1: Create database
|
|
db = Database('test.db')
|
|
|
|
# Step 2: Create user
|
|
user = User(name='Alice', email='alice@example.com')
|
|
db.save(user)
|
|
|
|
# Step 3: Verify creation
|
|
assert db.get_user('Alice').email == 'alice@example.com'
|
|
""",
|
|
"language": "python",
|
|
"category": "workflow",
|
|
"test_name": "test_user_creation_workflow",
|
|
"file_path": "tests/test_user.py",
|
|
}
|
|
|
|
steps, metadata = self.analyzer.analyze_workflow(workflow)
|
|
|
|
# Should extract 3 steps
|
|
self.assertGreaterEqual(len(steps), 2)
|
|
|
|
# Check step structure
|
|
self.assertIsInstance(steps[0], WorkflowStep)
|
|
self.assertEqual(steps[0].step_number, 1)
|
|
self.assertIsNotNone(steps[0].description)
|
|
|
|
# Check metadata
|
|
self.assertIn("complexity_level", metadata)
|
|
self.assertIn(metadata["complexity_level"], ["beginner", "intermediate", "advanced"])
|
|
|
|
def test_detect_prerequisites(self):
|
|
"""Test detection of prerequisites from imports and fixtures"""
|
|
workflow = {
|
|
"code": """
|
|
import pytest
|
|
from myapp import Database, User
|
|
|
|
@pytest.fixture
|
|
def db():
|
|
return Database('test.db')
|
|
|
|
def test_workflow(db):
|
|
user = User(name='Bob')
|
|
db.save(user)
|
|
""",
|
|
"language": "python",
|
|
"category": "workflow",
|
|
"test_name": "test_workflow",
|
|
"file_path": "tests/test.py",
|
|
}
|
|
|
|
steps, metadata = self.analyzer.analyze_workflow(workflow)
|
|
|
|
# Should analyze workflow successfully
|
|
self.assertIsInstance(steps, list)
|
|
self.assertIsInstance(metadata, dict)
|
|
# Prerequisites detection is internal - just verify it completes
|
|
|
|
def test_find_verification_points(self):
|
|
"""Test finding verification/assertion points in workflow"""
|
|
code = """
|
|
def test_workflow():
|
|
result = calculate(5, 3)
|
|
assert result == 8 # Verify calculation
|
|
|
|
status = save_to_db(result)
|
|
assert status == True # Verify save
|
|
"""
|
|
|
|
verifications = self.analyzer._find_verification_points(code)
|
|
|
|
# Should find assertion patterns
|
|
self.assertGreaterEqual(len(verifications), 0)
|
|
|
|
def test_calculate_complexity(self):
|
|
"""Test complexity level calculation"""
|
|
# Simple workflow - beginner
|
|
simple_steps = [
|
|
WorkflowStep(1, "x = 1", "Assign variable"),
|
|
WorkflowStep(2, "print(x)", "Print variable"),
|
|
]
|
|
simple_workflow = {"code": "x = 1\nprint(x)", "category": "workflow"}
|
|
complexity_simple = self.analyzer._calculate_complexity(simple_steps, simple_workflow)
|
|
self.assertEqual(complexity_simple, "beginner")
|
|
|
|
# Complex workflow - advanced
|
|
complex_steps = [WorkflowStep(i, f"step{i}", f"Step {i}") for i in range(1, 8)]
|
|
complex_workflow = {
|
|
"code": "\n".join(
|
|
[f"async def step{i}(): await complex_operation()" for i in range(7)]
|
|
),
|
|
"category": "workflow",
|
|
}
|
|
complexity_complex = self.analyzer._calculate_complexity(complex_steps, complex_workflow)
|
|
self.assertIn(complexity_complex, ["intermediate", "advanced"])
|
|
|
|
def test_extract_steps_python_ast(self):
|
|
"""Test Python AST-based step extraction"""
|
|
code = """
|
|
def test_workflow():
|
|
db = Database('test.db')
|
|
user = User(name='Alice')
|
|
db.save(user)
|
|
result = db.query('SELECT * FROM users')
|
|
assert len(result) == 1
|
|
"""
|
|
workflow = {
|
|
"code": code,
|
|
"language": "python",
|
|
"category": "workflow",
|
|
"test_name": "test_workflow",
|
|
"file_path": "test.py",
|
|
}
|
|
|
|
steps = self.analyzer._extract_steps_python(code, workflow)
|
|
|
|
# Should extract multiple steps
|
|
self.assertGreaterEqual(len(steps), 2)
|
|
|
|
# Each step should have required fields
|
|
for step in steps:
|
|
self.assertIsInstance(step.step_number, int)
|
|
self.assertIsInstance(step.code, str)
|
|
self.assertIsInstance(step.description, str)
|
|
|
|
def test_extract_steps_heuristic(self):
|
|
"""Test heuristic-based step extraction for non-Python languages"""
|
|
code = """
|
|
func TestWorkflow(t *testing.T) {
|
|
// Step 1
|
|
db := NewDatabase("test.db")
|
|
|
|
// Step 2
|
|
user := User{Name: "Alice"}
|
|
db.Save(user)
|
|
|
|
// Step 3
|
|
result := db.Query("SELECT * FROM users")
|
|
if len(result) != 1 {
|
|
t.Error("Expected 1 user")
|
|
}
|
|
}
|
|
"""
|
|
workflow = {
|
|
"code": code,
|
|
"language": "go",
|
|
"category": "workflow",
|
|
"test_name": "TestWorkflow",
|
|
"file_path": "test.go",
|
|
}
|
|
|
|
steps = self.analyzer._extract_steps_heuristic(code, workflow)
|
|
|
|
# Should extract steps based on comments or logical blocks
|
|
self.assertGreaterEqual(len(steps), 1)
|
|
|
|
|
|
class TestWorkflowGrouper(unittest.TestCase):
|
|
"""Tests for WorkflowGrouper - Group related workflows"""
|
|
|
|
def setUp(self):
|
|
self.grouper = WorkflowGrouper()
|
|
|
|
def test_group_by_file_path(self):
|
|
"""Test grouping workflows by file path"""
|
|
workflows = [
|
|
{
|
|
"test_name": "test_user_create",
|
|
"file_path": "tests/test_user.py",
|
|
"code": "user = User()",
|
|
"category": "workflow",
|
|
},
|
|
{
|
|
"test_name": "test_user_delete",
|
|
"file_path": "tests/test_user.py",
|
|
"code": "db.delete(user)",
|
|
"category": "workflow",
|
|
},
|
|
{
|
|
"test_name": "test_db_connect",
|
|
"file_path": "tests/test_database.py",
|
|
"code": "db = Database()",
|
|
"category": "workflow",
|
|
},
|
|
]
|
|
|
|
grouped = self.grouper._group_by_file_path(workflows)
|
|
|
|
# Should create 2 groups (test_user.py and test_database.py)
|
|
self.assertEqual(len(grouped), 2)
|
|
# Check that groups were created (titles are auto-generated from file names)
|
|
self.assertTrue(all(isinstance(k, str) for k in grouped))
|
|
|
|
def test_group_by_test_name(self):
|
|
"""Test grouping workflows by test name patterns"""
|
|
workflows = [
|
|
{"test_name": "test_user_create", "code": "user = User()", "category": "workflow"},
|
|
{"test_name": "test_user_update", "code": "user.update()", "category": "workflow"},
|
|
{"test_name": "test_admin_create", "code": "admin = Admin()", "category": "workflow"},
|
|
]
|
|
|
|
grouped = self.grouper._group_by_test_name(workflows)
|
|
|
|
# Should group by common prefix (test_user_*)
|
|
self.assertGreaterEqual(len(grouped), 1)
|
|
|
|
def test_group_by_complexity(self):
|
|
"""Test grouping workflows by complexity level"""
|
|
workflows = [
|
|
{
|
|
"test_name": "test_simple",
|
|
"code": "x = 1\nprint(x)",
|
|
"category": "workflow",
|
|
"complexity_level": "beginner",
|
|
},
|
|
{
|
|
"test_name": "test_complex",
|
|
"code": "\n".join(["step()" for _ in range(10)]),
|
|
"category": "workflow",
|
|
"complexity_level": "advanced",
|
|
},
|
|
]
|
|
|
|
grouped = self.grouper._group_by_complexity(workflows)
|
|
|
|
# Should create groups by complexity
|
|
self.assertGreaterEqual(len(grouped), 1)
|
|
|
|
def test_group_by_ai_tutorial_group(self):
|
|
"""Test AI-based tutorial grouping (or fallback if no AI)"""
|
|
workflows = [
|
|
{
|
|
"test_name": "test_user_create",
|
|
"code": 'user = User(name="Alice")',
|
|
"category": "workflow",
|
|
"file_path": "tests/test_user.py",
|
|
"tutorial_group": "User Management", # Simulated AI categorization
|
|
},
|
|
{
|
|
"test_name": "test_db_connect",
|
|
"code": "db = Database()",
|
|
"category": "workflow",
|
|
"file_path": "tests/test_db.py",
|
|
"tutorial_group": "Database Operations",
|
|
},
|
|
]
|
|
|
|
grouped = self.grouper._group_by_ai_tutorial_group(workflows)
|
|
|
|
# Should group by tutorial_group or fallback to file-path
|
|
self.assertGreaterEqual(len(grouped), 1)
|
|
|
|
|
|
class TestGuideGenerator(unittest.TestCase):
|
|
"""Tests for GuideGenerator - Generate markdown guides"""
|
|
|
|
def setUp(self):
|
|
self.generator = GuideGenerator()
|
|
|
|
def test_generate_guide_markdown(self):
|
|
"""Test generation of complete markdown guide"""
|
|
guide = HowToGuide(
|
|
guide_id="test-guide-1",
|
|
title="How to Create a User",
|
|
overview="This guide demonstrates user creation workflow",
|
|
complexity_level="beginner",
|
|
prerequisites=["Database", "User model"],
|
|
required_imports=["from myapp import Database, User"],
|
|
steps=[
|
|
WorkflowStep(1, 'db = Database("test.db")', "Create database connection"),
|
|
WorkflowStep(2, 'user = User(name="Alice")', "Create user object"),
|
|
WorkflowStep(3, "db.save(user)", "Save to database"),
|
|
],
|
|
use_case="Creating new users in the system",
|
|
tags=["user", "database", "create"],
|
|
)
|
|
|
|
markdown = self.generator.generate_guide_markdown(guide)
|
|
|
|
# Check markdown contains expected sections (actual format uses "# How To:" prefix)
|
|
self.assertIn("# How To:", markdown)
|
|
self.assertIn("How to Create a User", markdown)
|
|
self.assertIn("## Overview", markdown)
|
|
self.assertIn("## Prerequisites", markdown)
|
|
self.assertIn("Step 1:", markdown)
|
|
self.assertIn("Create database connection", markdown)
|
|
|
|
def test_create_header(self):
|
|
"""Test header generation with metadata"""
|
|
guide = HowToGuide(
|
|
guide_id="test-1",
|
|
title="Test Guide",
|
|
overview="Test",
|
|
complexity_level="beginner",
|
|
tags=["test", "example"],
|
|
)
|
|
|
|
header = self.generator._create_header(guide)
|
|
|
|
# Actual format uses "# How To:" prefix
|
|
self.assertIn("# How To:", header)
|
|
self.assertIn("Test Guide", header)
|
|
self.assertIn("Beginner", header)
|
|
|
|
def test_create_steps_section(self):
|
|
"""Test steps section generation"""
|
|
steps = [
|
|
WorkflowStep(
|
|
1,
|
|
"db = Database()",
|
|
"Create database",
|
|
expected_result="Database object",
|
|
verification="assert db.is_connected()",
|
|
),
|
|
WorkflowStep(2, "user = User()", "Create user"),
|
|
]
|
|
|
|
steps_md = self.generator._create_steps_section(steps)
|
|
|
|
# Actual format uses "## Step-by-Step Guide"
|
|
self.assertIn("## Step-by-Step Guide", steps_md)
|
|
self.assertIn("### Step 1:", steps_md)
|
|
self.assertIn("Create database", steps_md)
|
|
self.assertIn("```", steps_md) # Code block
|
|
self.assertIn("Database()", steps_md)
|
|
|
|
def test_create_complete_example(self):
|
|
"""Test complete example generation"""
|
|
guide = HowToGuide(
|
|
guide_id="test-1",
|
|
title="Test",
|
|
overview="Test",
|
|
complexity_level="beginner",
|
|
steps=[WorkflowStep(1, "x = 1", "Assign"), WorkflowStep(2, "print(x)", "Print")],
|
|
workflows=[{"code": "x = 1\nprint(x)", "language": "python"}],
|
|
)
|
|
|
|
example_md = self.generator._create_complete_example(guide)
|
|
|
|
self.assertIn("## Complete Example", example_md)
|
|
self.assertIn("```python", example_md)
|
|
|
|
def test_create_index(self):
|
|
"""Test index generation for guide collection"""
|
|
guides = [
|
|
HowToGuide(
|
|
guide_id="guide-1",
|
|
title="Beginner Guide",
|
|
overview="Simple guide",
|
|
complexity_level="beginner",
|
|
tags=["user"],
|
|
),
|
|
HowToGuide(
|
|
guide_id="guide-2",
|
|
title="Advanced Guide",
|
|
overview="Complex guide",
|
|
complexity_level="advanced",
|
|
tags=["admin", "security"],
|
|
),
|
|
]
|
|
|
|
# Method is actually called generate_index
|
|
index_md = self.generator.generate_index(guides)
|
|
|
|
self.assertIn("How-To Guides", index_md)
|
|
self.assertIn("Beginner Guide", index_md)
|
|
self.assertIn("Advanced Guide", index_md)
|
|
|
|
|
|
class TestHowToGuideBuilder(unittest.TestCase):
|
|
"""Tests for HowToGuideBuilder - Main orchestrator"""
|
|
|
|
def setUp(self):
|
|
self.builder = HowToGuideBuilder(enhance_with_ai=False)
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
|
|
def tearDown(self):
|
|
if os.path.exists(self.temp_dir):
|
|
shutil.rmtree(self.temp_dir)
|
|
|
|
def test_extract_workflow_examples(self):
|
|
"""Test extraction of workflow examples from mixed examples"""
|
|
examples = [
|
|
{
|
|
"category": "workflow",
|
|
"code": "db = Database()\nuser = User()\ndb.save(user)",
|
|
"test_name": "test_user_workflow",
|
|
"file_path": "tests/test_user.py",
|
|
"language": "python",
|
|
},
|
|
{
|
|
"category": "instantiation",
|
|
"code": "db = Database()",
|
|
"test_name": "test_db",
|
|
"file_path": "tests/test_db.py",
|
|
"language": "python",
|
|
},
|
|
]
|
|
|
|
workflows = self.builder._extract_workflow_examples(examples)
|
|
|
|
# Should only extract workflow category
|
|
self.assertEqual(len(workflows), 1)
|
|
self.assertEqual(workflows[0]["category"], "workflow")
|
|
|
|
def test_create_guide_from_workflows(self):
|
|
"""Test guide creation from grouped workflows"""
|
|
workflows = [
|
|
{
|
|
"code": 'user = User(name="Alice")\ndb.save(user)',
|
|
"test_name": "test_create_user",
|
|
"file_path": "tests/test_user.py",
|
|
"language": "python",
|
|
"category": "workflow",
|
|
}
|
|
]
|
|
|
|
guide = self.builder._create_guide("User Management", workflows)
|
|
|
|
self.assertIsInstance(guide, HowToGuide)
|
|
self.assertEqual(guide.title, "User Management")
|
|
self.assertGreater(len(guide.steps), 0)
|
|
self.assertIn(guide.complexity_level, ["beginner", "intermediate", "advanced"])
|
|
|
|
def test_create_collection(self):
|
|
"""Test guide collection creation with metadata"""
|
|
guides = [
|
|
HowToGuide(
|
|
guide_id="guide-1", title="Guide 1", overview="Test", complexity_level="beginner"
|
|
),
|
|
HowToGuide(
|
|
guide_id="guide-2", title="Guide 2", overview="Test", complexity_level="advanced"
|
|
),
|
|
]
|
|
|
|
collection = self.builder._create_collection(guides)
|
|
|
|
self.assertIsInstance(collection, GuideCollection)
|
|
self.assertEqual(collection.total_guides, 2)
|
|
# Attribute is guides_by_complexity not by_complexity
|
|
self.assertEqual(collection.guides_by_complexity["beginner"], 1)
|
|
self.assertEqual(collection.guides_by_complexity["advanced"], 1)
|
|
|
|
def test_save_guides_to_files(self):
|
|
"""Test saving guides to markdown files"""
|
|
guides = [
|
|
HowToGuide(
|
|
guide_id="test-guide",
|
|
title="Test Guide",
|
|
overview="Test overview",
|
|
complexity_level="beginner",
|
|
steps=[WorkflowStep(1, "x = 1", "Test step")],
|
|
)
|
|
]
|
|
|
|
# Correct attribute names
|
|
collection = GuideCollection(
|
|
total_guides=1,
|
|
guides=guides,
|
|
guides_by_complexity={"beginner": 1},
|
|
guides_by_use_case={},
|
|
)
|
|
|
|
output_dir = Path(self.temp_dir)
|
|
self.builder._save_guides_to_files(collection, output_dir)
|
|
|
|
# Check index file was created
|
|
self.assertTrue((output_dir / "index.md").exists())
|
|
|
|
# Check index content contains guide information
|
|
index_content = (output_dir / "index.md").read_text()
|
|
self.assertIn("Test Guide", index_content)
|
|
|
|
# Check that at least one markdown file exists
|
|
md_files = list(output_dir.glob("*.md"))
|
|
self.assertGreaterEqual(len(md_files), 1)
|
|
|
|
def test_build_guides_from_examples(self):
|
|
"""Test full guide building workflow"""
|
|
examples = [
|
|
{
|
|
"category": "workflow",
|
|
"code": """
|
|
def test_user_workflow():
|
|
db = Database('test.db')
|
|
user = User(name='Alice', email='alice@test.com')
|
|
db.save(user)
|
|
assert db.get_user('Alice').email == 'alice@test.com'
|
|
""",
|
|
"test_name": "test_user_workflow",
|
|
"file_path": "tests/test_user.py",
|
|
"language": "python",
|
|
"description": "User creation workflow",
|
|
"expected_behavior": "User should be saved and retrieved",
|
|
}
|
|
]
|
|
|
|
output_dir = Path(self.temp_dir) / "guides"
|
|
|
|
collection = self.builder.build_guides_from_examples(
|
|
examples, grouping_strategy="file-path", output_dir=output_dir
|
|
)
|
|
|
|
self.assertIsInstance(collection, GuideCollection)
|
|
self.assertGreater(collection.total_guides, 0)
|
|
self.assertTrue(output_dir.exists())
|
|
self.assertTrue((output_dir / "index.md").exists())
|
|
|
|
|
|
class TestEndToEnd(unittest.TestCase):
|
|
"""End-to-end integration test"""
|
|
|
|
def setUp(self):
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
|
|
def tearDown(self):
|
|
if os.path.exists(self.temp_dir):
|
|
shutil.rmtree(self.temp_dir)
|
|
|
|
def test_full_workflow(self):
|
|
"""Test complete workflow from examples to guides"""
|
|
# Create test examples JSON
|
|
examples = {
|
|
"total_examples": 2,
|
|
"examples": [
|
|
{
|
|
"category": "workflow",
|
|
"code": '''
|
|
def test_database_workflow():
|
|
"""Test complete database workflow"""
|
|
# Setup
|
|
db = Database('test.db')
|
|
|
|
# Create user
|
|
user = User(name='Alice', email='alice@example.com')
|
|
db.save(user)
|
|
|
|
# Verify
|
|
saved_user = db.get_user('Alice')
|
|
assert saved_user.email == 'alice@example.com'
|
|
''',
|
|
"test_name": "test_database_workflow",
|
|
"file_path": "tests/test_database.py",
|
|
"language": "python",
|
|
"description": "Complete database workflow",
|
|
"expected_behavior": "User saved and retrieved correctly",
|
|
},
|
|
{
|
|
"category": "workflow",
|
|
"code": '''
|
|
def test_authentication_workflow():
|
|
"""Test user authentication"""
|
|
user = User(name='Bob', password='secret123')
|
|
token = authenticate(user.name, 'secret123')
|
|
assert token is not None
|
|
assert verify_token(token) == user.name
|
|
''',
|
|
"test_name": "test_authentication_workflow",
|
|
"file_path": "tests/test_auth.py",
|
|
"language": "python",
|
|
"description": "Authentication workflow",
|
|
"expected_behavior": "User authenticated successfully",
|
|
},
|
|
],
|
|
}
|
|
|
|
# Save examples to temp file
|
|
examples_file = Path(self.temp_dir) / "test_examples.json"
|
|
with open(examples_file, "w") as f:
|
|
json.dump(examples, f)
|
|
|
|
# Build guides
|
|
builder = HowToGuideBuilder(enhance_with_ai=False)
|
|
output_dir = Path(self.temp_dir) / "tutorials"
|
|
|
|
collection = builder.build_guides_from_examples(
|
|
examples["examples"], grouping_strategy="file-path", output_dir=output_dir
|
|
)
|
|
|
|
# Verify results
|
|
self.assertIsInstance(collection, GuideCollection)
|
|
self.assertGreater(collection.total_guides, 0)
|
|
|
|
# Check output files
|
|
self.assertTrue(output_dir.exists())
|
|
self.assertTrue((output_dir / "index.md").exists())
|
|
|
|
# Check index content
|
|
index_content = (output_dir / "index.md").read_text()
|
|
self.assertIn("How-To Guides", index_content)
|
|
|
|
# Verify guide files exist (index.md + guide(s))
|
|
guide_files = list(output_dir.glob("*.md"))
|
|
self.assertGreaterEqual(len(guide_files), 1) # At least index.md or guides
|
|
|
|
|
|
class TestAIEnhancementIntegration(unittest.TestCase):
|
|
"""Tests for AI Enhancement integration with HowToGuideBuilder (C3.3)"""
|
|
|
|
def setUp(self):
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
|
|
def tearDown(self):
|
|
if os.path.exists(self.temp_dir):
|
|
shutil.rmtree(self.temp_dir)
|
|
|
|
def test_build_with_ai_enhancement_disabled(self):
|
|
"""Test building guides WITHOUT AI enhancement (backward compatibility)"""
|
|
examples = [
|
|
{
|
|
"example_id": "test_001",
|
|
"test_name": "test_user_registration",
|
|
"category": "workflow",
|
|
"code": """
|
|
def test_user_registration():
|
|
user = User.create(username="test", email="test@example.com")
|
|
assert user.id is not None
|
|
assert user.is_active is True
|
|
""",
|
|
"language": "python",
|
|
"file_path": "tests/test_user.py",
|
|
"line_start": 10,
|
|
"tags": ["authentication", "user"],
|
|
"ai_analysis": {
|
|
"tutorial_group": "User Management",
|
|
"best_practices": ["Validate email format"],
|
|
"common_mistakes": ["Not checking uniqueness"],
|
|
},
|
|
}
|
|
]
|
|
|
|
builder = HowToGuideBuilder()
|
|
output_dir = Path(self.temp_dir) / "guides"
|
|
|
|
# Build WITHOUT AI enhancement
|
|
collection = builder.build_guides_from_examples(
|
|
examples=examples,
|
|
grouping_strategy="ai-tutorial-group",
|
|
output_dir=output_dir,
|
|
enhance_with_ai=False,
|
|
ai_mode="none",
|
|
)
|
|
|
|
# Verify guides were created
|
|
self.assertIsInstance(collection, GuideCollection)
|
|
self.assertGreater(collection.total_guides, 0)
|
|
|
|
# Verify output files exist
|
|
self.assertTrue(output_dir.exists())
|
|
self.assertTrue((output_dir / "index.md").exists())
|
|
|
|
def test_build_with_ai_enhancement_api_mode_mocked(self):
|
|
"""Test building guides WITH AI enhancement in API mode (mocked)"""
|
|
from unittest.mock import patch
|
|
|
|
examples = [
|
|
{
|
|
"example_id": "test_002",
|
|
"test_name": "test_data_scraping",
|
|
"category": "workflow",
|
|
"code": """
|
|
def test_data_scraping():
|
|
scraper = DocumentationScraper()
|
|
result = scraper.scrape("https://example.com/docs")
|
|
assert result.pages > 0
|
|
""",
|
|
"language": "python",
|
|
"file_path": "tests/test_scraper.py",
|
|
"line_start": 20,
|
|
"tags": ["scraping", "documentation"],
|
|
"ai_analysis": {
|
|
"tutorial_group": "Data Collection",
|
|
"best_practices": ["Handle rate limiting"],
|
|
"common_mistakes": ["Not handling SSL errors"],
|
|
},
|
|
}
|
|
]
|
|
|
|
builder = HowToGuideBuilder()
|
|
output_dir = Path(self.temp_dir) / "guides_enhanced"
|
|
|
|
# Mock GuideEnhancer to avoid actual AI calls
|
|
with patch("skill_seekers.cli.guide_enhancer.GuideEnhancer") as MockEnhancer:
|
|
mock_enhancer = MockEnhancer.return_value
|
|
mock_enhancer.mode = "api"
|
|
|
|
# Mock the enhance_guide method to return enhanced data
|
|
def mock_enhance_guide(guide_data):
|
|
enhanced = guide_data.copy()
|
|
# Return proper StepEnhancement objects
|
|
enhanced["step_enhancements"] = [
|
|
StepEnhancement(step_index=0, explanation="Test explanation", variations=[])
|
|
]
|
|
enhanced["troubleshooting_detailed"] = []
|
|
enhanced["prerequisites_detailed"] = []
|
|
enhanced["next_steps_detailed"] = []
|
|
enhanced["use_cases"] = []
|
|
return enhanced
|
|
|
|
mock_enhancer.enhance_guide = mock_enhance_guide
|
|
|
|
# Build WITH AI enhancement
|
|
collection = builder.build_guides_from_examples(
|
|
examples=examples,
|
|
grouping_strategy="ai-tutorial-group",
|
|
output_dir=output_dir,
|
|
enhance_with_ai=True,
|
|
ai_mode="api",
|
|
)
|
|
|
|
# Verify guides were created
|
|
self.assertIsInstance(collection, GuideCollection)
|
|
self.assertGreater(collection.total_guides, 0)
|
|
|
|
# Verify enhancer was initialized
|
|
MockEnhancer.assert_called_once_with(mode="api")
|
|
|
|
def test_build_with_ai_enhancement_local_mode_mocked(self):
|
|
"""Test building guides WITH AI enhancement in LOCAL mode (mocked)"""
|
|
from unittest.mock import patch
|
|
|
|
examples = [
|
|
{
|
|
"example_id": "test_003",
|
|
"test_name": "test_api_integration",
|
|
"category": "workflow",
|
|
"code": """
|
|
def test_api_integration():
|
|
client = APIClient(base_url="https://api.example.com")
|
|
response = client.get("/users")
|
|
assert response.status_code == 200
|
|
""",
|
|
"language": "python",
|
|
"file_path": "tests/test_api.py",
|
|
"line_start": 30,
|
|
"tags": ["api", "integration"],
|
|
"ai_analysis": {
|
|
"tutorial_group": "API Testing",
|
|
"best_practices": ["Use environment variables"],
|
|
"common_mistakes": ["Hardcoded credentials"],
|
|
},
|
|
}
|
|
]
|
|
|
|
builder = HowToGuideBuilder()
|
|
output_dir = Path(self.temp_dir) / "guides_local"
|
|
|
|
# Mock GuideEnhancer for LOCAL mode
|
|
with patch("skill_seekers.cli.guide_enhancer.GuideEnhancer") as MockEnhancer:
|
|
mock_enhancer = MockEnhancer.return_value
|
|
mock_enhancer.mode = "local"
|
|
|
|
# Mock the enhance_guide method
|
|
def mock_enhance_guide(guide_data):
|
|
enhanced = guide_data.copy()
|
|
enhanced["step_enhancements"] = []
|
|
enhanced["troubleshooting_detailed"] = []
|
|
enhanced["prerequisites_detailed"] = []
|
|
enhanced["next_steps_detailed"] = []
|
|
enhanced["use_cases"] = []
|
|
return enhanced
|
|
|
|
mock_enhancer.enhance_guide = mock_enhance_guide
|
|
|
|
# Build WITH AI enhancement (LOCAL mode)
|
|
collection = builder.build_guides_from_examples(
|
|
examples=examples,
|
|
grouping_strategy="ai-tutorial-group",
|
|
output_dir=output_dir,
|
|
enhance_with_ai=True,
|
|
ai_mode="local",
|
|
)
|
|
|
|
# Verify guides were created
|
|
self.assertIsInstance(collection, GuideCollection)
|
|
self.assertGreater(collection.total_guides, 0)
|
|
|
|
# Verify LOCAL mode was used
|
|
MockEnhancer.assert_called_once_with(mode="local")
|
|
|
|
def test_build_with_ai_enhancement_auto_mode(self):
|
|
"""Test building guides WITH AI enhancement in AUTO mode"""
|
|
from unittest.mock import patch
|
|
|
|
examples = [
|
|
{
|
|
"example_id": "test_004",
|
|
"test_name": "test_database_migration",
|
|
"category": "workflow",
|
|
"code": """
|
|
def test_database_migration():
|
|
migrator = DatabaseMigrator()
|
|
migrator.run_migrations()
|
|
assert migrator.current_version == "2.0"
|
|
""",
|
|
"language": "python",
|
|
"file_path": "tests/test_db.py",
|
|
"line_start": 40,
|
|
"tags": ["database", "migration"],
|
|
"ai_analysis": {
|
|
"tutorial_group": "Database Operations",
|
|
"best_practices": ["Backup before migration"],
|
|
"common_mistakes": ["Not testing rollback"],
|
|
},
|
|
}
|
|
]
|
|
|
|
builder = HowToGuideBuilder()
|
|
output_dir = Path(self.temp_dir) / "guides_auto"
|
|
|
|
# Mock GuideEnhancer for AUTO mode
|
|
with patch("skill_seekers.cli.guide_enhancer.GuideEnhancer") as MockEnhancer:
|
|
mock_enhancer = MockEnhancer.return_value
|
|
mock_enhancer.mode = "local" # AUTO mode detected LOCAL
|
|
|
|
def mock_enhance_guide(guide_data):
|
|
enhanced = guide_data.copy()
|
|
enhanced["step_enhancements"] = []
|
|
enhanced["troubleshooting_detailed"] = []
|
|
enhanced["prerequisites_detailed"] = []
|
|
enhanced["next_steps_detailed"] = []
|
|
enhanced["use_cases"] = []
|
|
return enhanced
|
|
|
|
mock_enhancer.enhance_guide = mock_enhance_guide
|
|
|
|
# Build WITH AI enhancement (AUTO mode)
|
|
collection = builder.build_guides_from_examples(
|
|
examples=examples,
|
|
grouping_strategy="ai-tutorial-group",
|
|
output_dir=output_dir,
|
|
enhance_with_ai=True,
|
|
ai_mode="auto",
|
|
)
|
|
|
|
# Verify guides were created
|
|
self.assertIsInstance(collection, GuideCollection)
|
|
self.assertGreater(collection.total_guides, 0)
|
|
|
|
# Verify AUTO mode was used
|
|
MockEnhancer.assert_called_once_with(mode="auto")
|
|
|
|
def test_graceful_fallback_when_ai_fails(self):
|
|
"""Test graceful fallback when AI enhancement fails"""
|
|
from unittest.mock import patch
|
|
|
|
examples = [
|
|
{
|
|
"example_id": "test_005",
|
|
"test_name": "test_file_processing",
|
|
"category": "workflow",
|
|
"code": """
|
|
def test_file_processing():
|
|
processor = FileProcessor()
|
|
result = processor.process("data.csv")
|
|
assert result.rows == 100
|
|
""",
|
|
"language": "python",
|
|
"file_path": "tests/test_files.py",
|
|
"line_start": 50,
|
|
"tags": ["files", "processing"],
|
|
"ai_analysis": {
|
|
"tutorial_group": "Data Processing",
|
|
"best_practices": ["Validate file format"],
|
|
"common_mistakes": ["Not handling encoding"],
|
|
},
|
|
}
|
|
]
|
|
|
|
builder = HowToGuideBuilder()
|
|
output_dir = Path(self.temp_dir) / "guides_fallback"
|
|
|
|
# Mock GuideEnhancer to raise exception
|
|
with patch(
|
|
"skill_seekers.cli.guide_enhancer.GuideEnhancer",
|
|
side_effect=Exception("AI unavailable"),
|
|
):
|
|
# Should NOT crash - graceful fallback
|
|
collection = builder.build_guides_from_examples(
|
|
examples=examples,
|
|
grouping_strategy="ai-tutorial-group",
|
|
output_dir=output_dir,
|
|
enhance_with_ai=True,
|
|
ai_mode="api",
|
|
)
|
|
|
|
# Verify guides were still created (without enhancement)
|
|
self.assertIsInstance(collection, GuideCollection)
|
|
self.assertGreater(collection.total_guides, 0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|