#!/usr/bin/env python3 """ Tests for workflow_manager.py validate command Run with: python3 -m pytest tests/test_workflow_validate.py -v Or: python3 tests/test_workflow_validate.py """ import os import sys import tempfile import shutil import unittest from pathlib import Path from unittest.mock import patch, MagicMock # Add scripts directory to path sys.path.insert(0, str(Path(__file__).parent.parent / 'scripts')) from workflow_manager import ( validate_implementation, show_validation_checklist, ) class TestValidateImplementation(unittest.TestCase): """Test validate_implementation function.""" def setUp(self): """Set up test fixtures.""" self.temp_dir = tempfile.mkdtemp() self.original_cwd = os.getcwd() os.chdir(self.temp_dir) # Create .workflow directory structure self.workflow_dir = Path('.workflow') self.workflow_dir.mkdir() def tearDown(self): """Clean up test fixtures.""" os.chdir(self.original_cwd) shutil.rmtree(self.temp_dir) def test_no_active_workflow(self): """Test validation with no active workflow.""" result = validate_implementation() self.assertIn('errors', result) self.assertTrue( any('No active workflow' in e or 'not found' in e for e in result['errors']) ) def test_no_active_version(self): """Test validation with current.yml but no active version.""" current_yml = self.workflow_dir / 'current.yml' current_yml.write_text('some_other_key: value\n') result = validate_implementation() self.assertIn('errors', result) def test_missing_design_document(self): """Test validation when design document is missing.""" # Create current.yml with active version current_yml = self.workflow_dir / 'current.yml' current_yml.write_text('active_version: v001\n') # Create version directory without design document version_dir = self.workflow_dir / 'versions' / 'v001' version_dir.mkdir(parents=True) result = validate_implementation() self.assertIn('errors', result) self.assertTrue( any('Design document not found' in e or 'not found' in e for e in result['errors']) ) class TestValidateImplementationWithDesign(unittest.TestCase): """Test validate_implementation with a valid design document.""" def setUp(self): """Set up test fixtures with design document.""" self.temp_dir = tempfile.mkdtemp() self.original_cwd = os.getcwd() os.chdir(self.temp_dir) # Create .workflow directory structure self.workflow_dir = Path('.workflow') self.workflow_dir.mkdir() # Create current.yml current_yml = self.workflow_dir / 'current.yml' current_yml.write_text('active_version: v001\n') # Create version directory with design document version_dir = self.workflow_dir / 'versions' / 'v001' / 'design' version_dir.mkdir(parents=True) # Create minimal design document design_doc = version_dir / 'design_document.yml' design_doc.write_text(""" components: - id: component_button name: Button props: - name: label type: string required: true events: - name: onClick payload: void api_endpoints: - id: api_get_users method: GET path: /api/users responses: - status: 200 schema: properties: - name: users type: array data_models: - id: model_user name: User fields: - name: id type: uuid - name: email type: string """) def tearDown(self): """Clean up test fixtures.""" os.chdir(self.original_cwd) shutil.rmtree(self.temp_dir) def test_component_not_found(self): """Test validation when component file doesn't exist.""" result = validate_implementation() self.assertIn('errors', result) # Should report missing component file self.assertTrue( any('Button' in e and 'not found' in e for e in result['errors']) ) def test_component_found_with_correct_imports(self): """Test validation when component exists with correct imports.""" # Create component file comp_dir = Path('app/components') comp_dir.mkdir(parents=True) button_tsx = comp_dir / 'Button.tsx' button_tsx.write_text(""" import type { ButtonProps } from '@/types/component-props'; export function Button({ label, onClick }: ButtonProps) { return ; } """) result = validate_implementation() # Should pass the import check self.assertTrue( any('Props imported' in p and 'Button' in p for p in result['passed']) ) # Should pass the event check self.assertTrue( any('onClick' in p and 'implemented' in p for p in result['passed']) ) def test_component_with_inline_interface(self): """Test validation detects inline interface definition.""" # Create component file with inline interface (bad practice) comp_dir = Path('app/components') comp_dir.mkdir(parents=True) button_tsx = comp_dir / 'Button.tsx' button_tsx.write_text(""" interface ButtonProps { label: string; onClick?: () => void; } export function Button({ label, onClick }: ButtonProps) { return ; } """) result = validate_implementation() # Should report error for inline interface self.assertTrue( any('Defines own props' in e or 'should import' in e for e in result['errors']) ) def test_api_route_not_found(self): """Test validation when API route file doesn't exist.""" result = validate_implementation() # Should report missing route self.assertTrue( any('Route file not found' in e or 'api_get_users' in e for e in result['errors']) ) def test_api_route_found_with_correct_handler(self): """Test validation when API route exists with correct handler.""" # Create API route file api_dir = Path('app/api/users') api_dir.mkdir(parents=True) route_ts = api_dir / 'route.ts' route_ts.write_text(""" import type { GetUsersResponse } from '@/types/api-types'; export async function GET(request: Request) { const users = await getUsers(); return Response.json({ users }); } """) result = validate_implementation() # Should pass the handler check self.assertTrue( any('GET handler found' in p for p in result['passed']) ) def test_prisma_schema_not_found(self): """Test validation when Prisma schema doesn't exist.""" result = validate_implementation() # Should warn about missing Prisma schema self.assertTrue( any('prisma/schema.prisma not found' in w for w in result['warnings']) ) def test_prisma_model_found(self): """Test validation when Prisma model exists.""" # Create Prisma schema prisma_dir = Path('prisma') prisma_dir.mkdir() schema = prisma_dir / 'schema.prisma' schema.write_text(""" model User { id String @id @default(uuid()) email String @unique } """) result = validate_implementation() # Should pass model check self.assertTrue( any('User' in p and 'exists' in p for p in result['passed']) ) class TestShowValidationChecklist(unittest.TestCase): """Test show_validation_checklist function.""" def test_display_passed(self): """Test display of passed checks.""" result = { 'passed': ['Check 1 passed', 'Check 2 passed'], 'warnings': [], 'errors': [], 'stats': {'components_checked': 1, 'apis_checked': 1, 'models_checked': 1} } # Should not raise any exceptions show_validation_checklist(result) def test_display_with_errors(self): """Test display with errors.""" result = { 'passed': ['Check 1 passed'], 'warnings': ['Warning 1'], 'errors': ['Error 1', 'Error 2'], 'stats': {'components_checked': 1, 'apis_checked': 0, 'models_checked': 0} } # Should not raise any exceptions show_validation_checklist(result) def test_display_empty_result(self): """Test display with empty results.""" result = { 'passed': [], 'warnings': [], 'errors': [], 'stats': {'components_checked': 0, 'apis_checked': 0, 'models_checked': 0} } # Should not raise any exceptions show_validation_checklist(result) class TestValidationStats(unittest.TestCase): """Test validation statistics.""" def setUp(self): """Set up test fixtures.""" self.temp_dir = tempfile.mkdtemp() self.original_cwd = os.getcwd() os.chdir(self.temp_dir) # Create minimal workflow structure workflow_dir = Path('.workflow') workflow_dir.mkdir() (workflow_dir / 'current.yml').write_text('active_version: v001\n') version_dir = workflow_dir / 'versions' / 'v001' / 'design' version_dir.mkdir(parents=True) (version_dir / 'design_document.yml').write_text(""" components: - id: comp1 name: Comp1 props: [] events: [] - id: comp2 name: Comp2 props: [] events: [] api_endpoints: - id: api1 method: GET path: /api/test responses: [] data_models: - id: model1 name: Model1 fields: [] """) def tearDown(self): """Clean up.""" os.chdir(self.original_cwd) shutil.rmtree(self.temp_dir) def test_stats_count(self): """Test that stats count entities correctly.""" result = validate_implementation() self.assertEqual(result['stats']['components_checked'], 2) self.assertEqual(result['stats']['apis_checked'], 1) self.assertEqual(result['stats']['models_checked'], 0) # No prisma schema if __name__ == '__main__': unittest.main()