""" Test fixtures and sample data for the Claude Code Project Tracker. """ from datetime import datetime, timedelta from typing import Dict, List, Any from faker import Faker fake = Faker() class TestDataFactory: """Factory for creating realistic test data.""" @staticmethod def create_project_data(**overrides) -> Dict[str, Any]: """Create sample project data.""" data = { "name": fake.company(), "path": fake.file_path(depth=3, extension=""), "git_repo": fake.url().replace("http://", "https://github.com/"), "languages": fake.random_elements( elements=["python", "javascript", "typescript", "go", "rust", "java", "cpp"], length=fake.random_int(min=1, max=4), unique=True ), "created_at": fake.date_time_between(start_date="-1y", end_date="now"), "last_session": fake.date_time_between(start_date="-30d", end_date="now"), "total_sessions": fake.random_int(min=1, max=50), "total_time_minutes": fake.random_int(min=30, max=2000), "files_modified_count": fake.random_int(min=5, max=100), "lines_changed_count": fake.random_int(min=100, max=5000) } data.update(overrides) return data @staticmethod def create_session_data(project_id: int = 1, **overrides) -> Dict[str, Any]: """Create sample session data.""" start_time = fake.date_time_between(start_date="-7d", end_date="now") duration = fake.random_int(min=5, max=180) # 5 minutes to 3 hours data = { "project_id": project_id, "start_time": start_time, "end_time": start_time + timedelta(minutes=duration), "session_type": fake.random_element(elements=["startup", "resume", "clear"]), "working_directory": fake.file_path(depth=3, extension=""), "git_branch": fake.random_element(elements=["main", "develop", "feature/new-feature", "bugfix/issue-123"]), "environment": { "user": fake.user_name(), "pwd": fake.file_path(depth=3, extension=""), "python_version": "3.11.0", "node_version": "18.17.0" }, "duration_minutes": duration, "activity_count": fake.random_int(min=3, max=50), "conversation_count": fake.random_int(min=2, max=30), "files_touched": [fake.file_path() for _ in range(fake.random_int(min=1, max=8))] } data.update(overrides) return data @staticmethod def create_conversation_data(session_id: int = 1, **overrides) -> Dict[str, Any]: """Create sample conversation data.""" prompts = [ "How do I implement user authentication?", "Can you help me debug this error?", "What's the best way to structure this code?", "Help me optimize this function for performance", "How do I add tests for this component?", "Can you review this code for best practices?", "What libraries should I use for this feature?", "How do I handle errors in this async function?" ] responses = [ "I can help you implement user authentication. Here's a comprehensive approach...", "Let me analyze this error and provide a solution...", "For code structure, I recommend following these patterns...", "Here are several optimization strategies for your function...", "Let's add comprehensive tests for this component...", "I've reviewed your code and here are my recommendations...", "For this feature, I suggest these well-maintained libraries...", "Here's how to properly handle errors in async functions..." ] data = { "session_id": session_id, "timestamp": fake.date_time_between(start_date="-7d", end_date="now"), "user_prompt": fake.random_element(elements=prompts), "claude_response": fake.random_element(elements=responses), "tools_used": fake.random_elements( elements=["Edit", "Write", "Read", "Bash", "Grep", "Glob", "Task"], length=fake.random_int(min=1, max=4), unique=True ), "files_affected": [ fake.file_path(extension=ext) for ext in fake.random_elements( elements=["py", "js", "ts", "go", "rs", "java", "cpp", "md"], length=fake.random_int(min=0, max=3), unique=True ) ], "context": { "intent": fake.random_element(elements=["debugging", "implementation", "learning", "optimization"]), "complexity": fake.random_element(elements=["low", "medium", "high"]) }, "tokens_input": fake.random_int(min=50, max=500), "tokens_output": fake.random_int(min=100, max=1000), "exchange_type": fake.random_element(elements=["user_prompt", "claude_response"]) } data.update(overrides) return data @staticmethod def create_activity_data(session_id: int = 1, **overrides) -> Dict[str, Any]: """Create sample activity data.""" tools = { "Edit": { "action": "file_edit", "metadata": {"lines_changed": fake.random_int(min=1, max=50)}, "lines_added": fake.random_int(min=0, max=30), "lines_removed": fake.random_int(min=0, max=20) }, "Write": { "action": "file_write", "metadata": {"new_file": fake.boolean()}, "lines_added": fake.random_int(min=10, max=100), "lines_removed": 0 }, "Read": { "action": "file_read", "metadata": {"file_size": fake.random_int(min=100, max=5000)}, "lines_added": 0, "lines_removed": 0 }, "Bash": { "action": "command_execution", "metadata": { "command": fake.random_element(elements=[ "npm install", "pytest", "git status", "python main.py", "docker build ." ]), "exit_code": 0 }, "lines_added": 0, "lines_removed": 0 }, "Grep": { "action": "search", "metadata": { "pattern": fake.word(), "matches_found": fake.random_int(min=0, max=20) }, "lines_added": 0, "lines_removed": 0 } } tool_name = fake.random_element(elements=list(tools.keys())) tool_config = tools[tool_name] data = { "session_id": session_id, "timestamp": fake.date_time_between(start_date="-7d", end_date="now"), "tool_name": tool_name, "action": tool_config["action"], "file_path": fake.file_path() if tool_name in ["Edit", "Write", "Read"] else None, "metadata": tool_config["metadata"], "success": fake.boolean(chance_of_getting_true=90), "error_message": None if fake.boolean(chance_of_getting_true=90) else "Operation failed", "lines_added": tool_config.get("lines_added", 0), "lines_removed": tool_config.get("lines_removed", 0) } data.update(overrides) return data @staticmethod def create_waiting_period_data(session_id: int = 1, **overrides) -> Dict[str, Any]: """Create sample waiting period data.""" start_time = fake.date_time_between(start_date="-7d", end_date="now") duration = fake.random_int(min=5, max=300) # 5 seconds to 5 minutes activities = { "thinking": "User is contemplating the response", "research": "User is looking up documentation", "external_work": "User is working in another application", "break": "User stepped away from the computer" } likely_activity = fake.random_element(elements=list(activities.keys())) data = { "session_id": session_id, "start_time": start_time, "end_time": start_time + timedelta(seconds=duration), "duration_seconds": duration, "context_before": "Claude finished providing a detailed explanation", "context_after": fake.random_element(elements=[ "User asked a follow-up question", "User requested clarification", "User provided additional context", "User asked about a different topic" ]), "likely_activity": likely_activity } data.update(overrides) return data @staticmethod def create_git_operation_data(session_id: int = 1, **overrides) -> Dict[str, Any]: """Create sample git operation data.""" operations = { "commit": { "command": "git commit -m 'Add new feature'", "result": "[main abc123] Add new feature\\n 2 files changed, 15 insertions(+), 3 deletions(-)", "files_changed": ["main.py", "utils.py"], "lines_added": 15, "lines_removed": 3, "commit_hash": fake.sha1()[:8] }, "push": { "command": "git push origin main", "result": "To https://github.com/user/repo.git\\n abc123..def456 main -> main", "files_changed": [], "lines_added": 0, "lines_removed": 0, "commit_hash": None }, "pull": { "command": "git pull origin main", "result": "Already up to date.", "files_changed": [], "lines_added": 0, "lines_removed": 0, "commit_hash": None }, "branch": { "command": "git checkout -b feature/new-feature", "result": "Switched to a new branch 'feature/new-feature'", "files_changed": [], "lines_added": 0, "lines_removed": 0, "commit_hash": None } } operation = fake.random_element(elements=list(operations.keys())) op_config = operations[operation] data = { "session_id": session_id, "timestamp": fake.date_time_between(start_date="-7d", end_date="now"), "operation": operation, "command": op_config["command"], "result": op_config["result"], "success": fake.boolean(chance_of_getting_true=95), "files_changed": op_config["files_changed"], "lines_added": op_config["lines_added"], "lines_removed": op_config["lines_removed"], "commit_hash": op_config["commit_hash"], "branch_from": fake.random_element(elements=["main", "develop", "feature/old-feature"]) if operation == "merge" else None, "branch_to": fake.random_element(elements=["main", "develop"]) if operation == "merge" else None } data.update(overrides) return data class SampleDataSets: """Pre-defined sample data sets for different test scenarios.""" @staticmethod def productive_session() -> Dict[str, List[Dict[str, Any]]]: """Data for a highly productive development session.""" project_data = TestDataFactory.create_project_data( name="E-commerce Platform", languages=["python", "javascript", "typescript"], total_sessions=25, total_time_minutes=800 ) session_data = TestDataFactory.create_session_data( session_type="startup", duration_minutes=120, activity_count=45, conversation_count=12 ) conversations = [ TestDataFactory.create_conversation_data( user_prompt="How do I implement user authentication with JWT?", tools_used=["Edit", "Write"], files_affected=["auth.py", "models.py"] ), TestDataFactory.create_conversation_data( user_prompt="Can you help me optimize this database query?", tools_used=["Edit", "Read"], files_affected=["queries.py"] ), TestDataFactory.create_conversation_data( user_prompt="What's the best way to handle async errors?", tools_used=["Edit"], files_affected=["error_handler.py"] ) ] activities = [ TestDataFactory.create_activity_data(tool_name="Edit", lines_added=25, lines_removed=5), TestDataFactory.create_activity_data(tool_name="Write", lines_added=50, lines_removed=0), TestDataFactory.create_activity_data(tool_name="Bash", metadata={"command": "pytest"}), TestDataFactory.create_activity_data(tool_name="Read", file_path="/docs/api.md") ] return { "project": project_data, "session": session_data, "conversations": conversations, "activities": activities } @staticmethod def debugging_session() -> Dict[str, List[Dict[str, Any]]]: """Data for a debugging-focused session with lots of investigation.""" project_data = TestDataFactory.create_project_data( name="Bug Tracking System", languages=["go", "typescript"] ) session_data = TestDataFactory.create_session_data( session_type="resume", duration_minutes=90, activity_count=35, conversation_count=8 ) conversations = [ TestDataFactory.create_conversation_data( user_prompt="I'm getting a panic in my Go application, can you help debug?", tools_used=["Read", "Grep"], files_affected=["main.go", "handler.go"], context={"intent": "debugging", "complexity": "high"} ), TestDataFactory.create_conversation_data( user_prompt="The tests are failing intermittently, what could be wrong?", tools_used=["Read", "Bash"], files_affected=["test_handler.go"] ) ] # Lots of read operations for debugging activities = [ TestDataFactory.create_activity_data(tool_name="Read") for _ in range(8) ] + [ TestDataFactory.create_activity_data(tool_name="Grep", metadata={"pattern": "error", "matches_found": 12}), TestDataFactory.create_activity_data(tool_name="Bash", metadata={"command": "go test -v"}), TestDataFactory.create_activity_data(tool_name="Edit", lines_added=3, lines_removed=1) ] # Longer thinking periods during debugging waiting_periods = [ TestDataFactory.create_waiting_period_data( duration_seconds=180, likely_activity="research", context_after="User asked about error patterns" ), TestDataFactory.create_waiting_period_data( duration_seconds=120, likely_activity="thinking", context_after="User provided stack trace" ) ] return { "project": project_data, "session": session_data, "conversations": conversations, "activities": activities, "waiting_periods": waiting_periods } @staticmethod def learning_session() -> Dict[str, List[Dict[str, Any]]]: """Data for a learning-focused session with lots of questions.""" project_data = TestDataFactory.create_project_data( name="Learning Rust", languages=["rust"], total_sessions=5, total_time_minutes=200 ) conversations = [ TestDataFactory.create_conversation_data( user_prompt="What's the difference between String and &str in Rust?", context={"intent": "learning", "complexity": "medium"} ), TestDataFactory.create_conversation_data( user_prompt="How do I handle ownership and borrowing correctly?", context={"intent": "learning", "complexity": "high"} ), TestDataFactory.create_conversation_data( user_prompt="Can you explain Rust's trait system?", context={"intent": "learning", "complexity": "high"} ) ] return { "project": project_data, "conversations": conversations }