attune-ai 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- attune/__init__.py +358 -0
- attune/adaptive/__init__.py +13 -0
- attune/adaptive/task_complexity.py +127 -0
- attune/agent_monitoring.py +414 -0
- attune/cache/__init__.py +117 -0
- attune/cache/base.py +166 -0
- attune/cache/dependency_manager.py +256 -0
- attune/cache/hash_only.py +251 -0
- attune/cache/hybrid.py +457 -0
- attune/cache/storage.py +285 -0
- attune/cache_monitor.py +356 -0
- attune/cache_stats.py +298 -0
- attune/cli/__init__.py +152 -0
- attune/cli/__main__.py +12 -0
- attune/cli/commands/__init__.py +1 -0
- attune/cli/commands/batch.py +264 -0
- attune/cli/commands/cache.py +248 -0
- attune/cli/commands/help.py +331 -0
- attune/cli/commands/info.py +140 -0
- attune/cli/commands/inspect.py +436 -0
- attune/cli/commands/inspection.py +57 -0
- attune/cli/commands/memory.py +48 -0
- attune/cli/commands/metrics.py +92 -0
- attune/cli/commands/orchestrate.py +184 -0
- attune/cli/commands/patterns.py +207 -0
- attune/cli/commands/profiling.py +202 -0
- attune/cli/commands/provider.py +98 -0
- attune/cli/commands/routing.py +285 -0
- attune/cli/commands/setup.py +96 -0
- attune/cli/commands/status.py +235 -0
- attune/cli/commands/sync.py +166 -0
- attune/cli/commands/tier.py +121 -0
- attune/cli/commands/utilities.py +114 -0
- attune/cli/commands/workflow.py +579 -0
- attune/cli/core.py +32 -0
- attune/cli/parsers/__init__.py +68 -0
- attune/cli/parsers/batch.py +118 -0
- attune/cli/parsers/cache.py +65 -0
- attune/cli/parsers/help.py +41 -0
- attune/cli/parsers/info.py +26 -0
- attune/cli/parsers/inspect.py +66 -0
- attune/cli/parsers/metrics.py +42 -0
- attune/cli/parsers/orchestrate.py +61 -0
- attune/cli/parsers/patterns.py +54 -0
- attune/cli/parsers/provider.py +40 -0
- attune/cli/parsers/routing.py +110 -0
- attune/cli/parsers/setup.py +42 -0
- attune/cli/parsers/status.py +47 -0
- attune/cli/parsers/sync.py +31 -0
- attune/cli/parsers/tier.py +33 -0
- attune/cli/parsers/workflow.py +77 -0
- attune/cli/utils/__init__.py +1 -0
- attune/cli/utils/data.py +242 -0
- attune/cli/utils/helpers.py +68 -0
- attune/cli_legacy.py +3957 -0
- attune/cli_minimal.py +1159 -0
- attune/cli_router.py +437 -0
- attune/cli_unified.py +814 -0
- attune/config/__init__.py +66 -0
- attune/config/xml_config.py +286 -0
- attune/config.py +545 -0
- attune/coordination.py +870 -0
- attune/core.py +1511 -0
- attune/core_modules/__init__.py +15 -0
- attune/cost_tracker.py +626 -0
- attune/dashboard/__init__.py +41 -0
- attune/dashboard/app.py +512 -0
- attune/dashboard/simple_server.py +435 -0
- attune/dashboard/standalone_server.py +547 -0
- attune/discovery.py +306 -0
- attune/emergence.py +306 -0
- attune/exceptions.py +123 -0
- attune/feedback_loops.py +373 -0
- attune/hot_reload/README.md +473 -0
- attune/hot_reload/__init__.py +62 -0
- attune/hot_reload/config.py +83 -0
- attune/hot_reload/integration.py +229 -0
- attune/hot_reload/reloader.py +298 -0
- attune/hot_reload/watcher.py +183 -0
- attune/hot_reload/websocket.py +177 -0
- attune/levels.py +577 -0
- attune/leverage_points.py +441 -0
- attune/logging_config.py +261 -0
- attune/mcp/__init__.py +10 -0
- attune/mcp/server.py +506 -0
- attune/memory/__init__.py +237 -0
- attune/memory/claude_memory.py +469 -0
- attune/memory/config.py +224 -0
- attune/memory/control_panel.py +1290 -0
- attune/memory/control_panel_support.py +145 -0
- attune/memory/cross_session.py +845 -0
- attune/memory/edges.py +179 -0
- attune/memory/encryption.py +159 -0
- attune/memory/file_session.py +770 -0
- attune/memory/graph.py +570 -0
- attune/memory/long_term.py +913 -0
- attune/memory/long_term_types.py +99 -0
- attune/memory/mixins/__init__.py +25 -0
- attune/memory/mixins/backend_init_mixin.py +249 -0
- attune/memory/mixins/capabilities_mixin.py +208 -0
- attune/memory/mixins/handoff_mixin.py +208 -0
- attune/memory/mixins/lifecycle_mixin.py +49 -0
- attune/memory/mixins/long_term_mixin.py +352 -0
- attune/memory/mixins/promotion_mixin.py +109 -0
- attune/memory/mixins/short_term_mixin.py +182 -0
- attune/memory/nodes.py +179 -0
- attune/memory/redis_bootstrap.py +540 -0
- attune/memory/security/__init__.py +31 -0
- attune/memory/security/audit_logger.py +932 -0
- attune/memory/security/pii_scrubber.py +640 -0
- attune/memory/security/secrets_detector.py +678 -0
- attune/memory/short_term.py +2192 -0
- attune/memory/simple_storage.py +302 -0
- attune/memory/storage/__init__.py +15 -0
- attune/memory/storage_backend.py +167 -0
- attune/memory/summary_index.py +583 -0
- attune/memory/types.py +446 -0
- attune/memory/unified.py +182 -0
- attune/meta_workflows/__init__.py +74 -0
- attune/meta_workflows/agent_creator.py +248 -0
- attune/meta_workflows/builtin_templates.py +567 -0
- attune/meta_workflows/cli_commands/__init__.py +56 -0
- attune/meta_workflows/cli_commands/agent_commands.py +321 -0
- attune/meta_workflows/cli_commands/analytics_commands.py +442 -0
- attune/meta_workflows/cli_commands/config_commands.py +232 -0
- attune/meta_workflows/cli_commands/memory_commands.py +182 -0
- attune/meta_workflows/cli_commands/template_commands.py +354 -0
- attune/meta_workflows/cli_commands/workflow_commands.py +382 -0
- attune/meta_workflows/cli_meta_workflows.py +59 -0
- attune/meta_workflows/form_engine.py +292 -0
- attune/meta_workflows/intent_detector.py +409 -0
- attune/meta_workflows/models.py +569 -0
- attune/meta_workflows/pattern_learner.py +738 -0
- attune/meta_workflows/plan_generator.py +384 -0
- attune/meta_workflows/session_context.py +397 -0
- attune/meta_workflows/template_registry.py +229 -0
- attune/meta_workflows/workflow.py +984 -0
- attune/metrics/__init__.py +12 -0
- attune/metrics/collector.py +31 -0
- attune/metrics/prompt_metrics.py +194 -0
- attune/models/__init__.py +172 -0
- attune/models/__main__.py +13 -0
- attune/models/adaptive_routing.py +437 -0
- attune/models/auth_cli.py +444 -0
- attune/models/auth_strategy.py +450 -0
- attune/models/cli.py +655 -0
- attune/models/empathy_executor.py +354 -0
- attune/models/executor.py +257 -0
- attune/models/fallback.py +762 -0
- attune/models/provider_config.py +282 -0
- attune/models/registry.py +472 -0
- attune/models/tasks.py +359 -0
- attune/models/telemetry/__init__.py +71 -0
- attune/models/telemetry/analytics.py +594 -0
- attune/models/telemetry/backend.py +196 -0
- attune/models/telemetry/data_models.py +431 -0
- attune/models/telemetry/storage.py +489 -0
- attune/models/token_estimator.py +420 -0
- attune/models/validation.py +280 -0
- attune/monitoring/__init__.py +52 -0
- attune/monitoring/alerts.py +946 -0
- attune/monitoring/alerts_cli.py +448 -0
- attune/monitoring/multi_backend.py +271 -0
- attune/monitoring/otel_backend.py +362 -0
- attune/optimization/__init__.py +19 -0
- attune/optimization/context_optimizer.py +272 -0
- attune/orchestration/__init__.py +67 -0
- attune/orchestration/agent_templates.py +707 -0
- attune/orchestration/config_store.py +499 -0
- attune/orchestration/execution_strategies.py +2111 -0
- attune/orchestration/meta_orchestrator.py +1168 -0
- attune/orchestration/pattern_learner.py +696 -0
- attune/orchestration/real_tools.py +931 -0
- attune/pattern_cache.py +187 -0
- attune/pattern_library.py +542 -0
- attune/patterns/debugging/all_patterns.json +81 -0
- attune/patterns/debugging/workflow_20260107_1770825e.json +77 -0
- attune/patterns/refactoring_memory.json +89 -0
- attune/persistence.py +564 -0
- attune/platform_utils.py +265 -0
- attune/plugins/__init__.py +28 -0
- attune/plugins/base.py +361 -0
- attune/plugins/registry.py +268 -0
- attune/project_index/__init__.py +32 -0
- attune/project_index/cli.py +335 -0
- attune/project_index/index.py +667 -0
- attune/project_index/models.py +504 -0
- attune/project_index/reports.py +474 -0
- attune/project_index/scanner.py +777 -0
- attune/project_index/scanner_parallel.py +291 -0
- attune/prompts/__init__.py +61 -0
- attune/prompts/config.py +77 -0
- attune/prompts/context.py +177 -0
- attune/prompts/parser.py +285 -0
- attune/prompts/registry.py +313 -0
- attune/prompts/templates.py +208 -0
- attune/redis_config.py +302 -0
- attune/redis_memory.py +799 -0
- attune/resilience/__init__.py +56 -0
- attune/resilience/circuit_breaker.py +256 -0
- attune/resilience/fallback.py +179 -0
- attune/resilience/health.py +300 -0
- attune/resilience/retry.py +209 -0
- attune/resilience/timeout.py +135 -0
- attune/routing/__init__.py +43 -0
- attune/routing/chain_executor.py +433 -0
- attune/routing/classifier.py +217 -0
- attune/routing/smart_router.py +234 -0
- attune/routing/workflow_registry.py +343 -0
- attune/scaffolding/README.md +589 -0
- attune/scaffolding/__init__.py +35 -0
- attune/scaffolding/__main__.py +14 -0
- attune/scaffolding/cli.py +240 -0
- attune/scaffolding/templates/base_wizard.py.jinja2 +121 -0
- attune/scaffolding/templates/coach_wizard.py.jinja2 +321 -0
- attune/scaffolding/templates/domain_wizard.py.jinja2 +408 -0
- attune/scaffolding/templates/linear_flow_wizard.py.jinja2 +203 -0
- attune/socratic/__init__.py +256 -0
- attune/socratic/ab_testing.py +958 -0
- attune/socratic/blueprint.py +533 -0
- attune/socratic/cli.py +703 -0
- attune/socratic/collaboration.py +1114 -0
- attune/socratic/domain_templates.py +924 -0
- attune/socratic/embeddings.py +738 -0
- attune/socratic/engine.py +794 -0
- attune/socratic/explainer.py +682 -0
- attune/socratic/feedback.py +772 -0
- attune/socratic/forms.py +629 -0
- attune/socratic/generator.py +732 -0
- attune/socratic/llm_analyzer.py +637 -0
- attune/socratic/mcp_server.py +702 -0
- attune/socratic/session.py +312 -0
- attune/socratic/storage.py +667 -0
- attune/socratic/success.py +730 -0
- attune/socratic/visual_editor.py +860 -0
- attune/socratic/web_ui.py +958 -0
- attune/telemetry/__init__.py +39 -0
- attune/telemetry/agent_coordination.py +475 -0
- attune/telemetry/agent_tracking.py +367 -0
- attune/telemetry/approval_gates.py +545 -0
- attune/telemetry/cli.py +1231 -0
- attune/telemetry/commands/__init__.py +14 -0
- attune/telemetry/commands/dashboard_commands.py +696 -0
- attune/telemetry/event_streaming.py +409 -0
- attune/telemetry/feedback_loop.py +567 -0
- attune/telemetry/usage_tracker.py +591 -0
- attune/templates.py +754 -0
- attune/test_generator/__init__.py +38 -0
- attune/test_generator/__main__.py +14 -0
- attune/test_generator/cli.py +234 -0
- attune/test_generator/generator.py +355 -0
- attune/test_generator/risk_analyzer.py +216 -0
- attune/test_generator/templates/unit_test.py.jinja2 +272 -0
- attune/tier_recommender.py +384 -0
- attune/tools.py +183 -0
- attune/trust/__init__.py +28 -0
- attune/trust/circuit_breaker.py +579 -0
- attune/trust_building.py +527 -0
- attune/validation/__init__.py +19 -0
- attune/validation/xml_validator.py +281 -0
- attune/vscode_bridge.py +173 -0
- attune/workflow_commands.py +780 -0
- attune/workflow_patterns/__init__.py +33 -0
- attune/workflow_patterns/behavior.py +249 -0
- attune/workflow_patterns/core.py +76 -0
- attune/workflow_patterns/output.py +99 -0
- attune/workflow_patterns/registry.py +255 -0
- attune/workflow_patterns/structural.py +288 -0
- attune/workflows/__init__.py +539 -0
- attune/workflows/autonomous_test_gen.py +1268 -0
- attune/workflows/base.py +2667 -0
- attune/workflows/batch_processing.py +342 -0
- attune/workflows/bug_predict.py +1084 -0
- attune/workflows/builder.py +273 -0
- attune/workflows/caching.py +253 -0
- attune/workflows/code_review.py +1048 -0
- attune/workflows/code_review_adapters.py +312 -0
- attune/workflows/code_review_pipeline.py +722 -0
- attune/workflows/config.py +645 -0
- attune/workflows/dependency_check.py +644 -0
- attune/workflows/document_gen/__init__.py +25 -0
- attune/workflows/document_gen/config.py +30 -0
- attune/workflows/document_gen/report_formatter.py +162 -0
- attune/workflows/document_gen/workflow.py +1426 -0
- attune/workflows/document_manager.py +216 -0
- attune/workflows/document_manager_README.md +134 -0
- attune/workflows/documentation_orchestrator.py +1205 -0
- attune/workflows/history.py +510 -0
- attune/workflows/keyboard_shortcuts/__init__.py +39 -0
- attune/workflows/keyboard_shortcuts/generators.py +391 -0
- attune/workflows/keyboard_shortcuts/parsers.py +416 -0
- attune/workflows/keyboard_shortcuts/prompts.py +295 -0
- attune/workflows/keyboard_shortcuts/schema.py +193 -0
- attune/workflows/keyboard_shortcuts/workflow.py +509 -0
- attune/workflows/llm_base.py +363 -0
- attune/workflows/manage_docs.py +87 -0
- attune/workflows/manage_docs_README.md +134 -0
- attune/workflows/manage_documentation.py +821 -0
- attune/workflows/new_sample_workflow1.py +149 -0
- attune/workflows/new_sample_workflow1_README.md +150 -0
- attune/workflows/orchestrated_health_check.py +849 -0
- attune/workflows/orchestrated_release_prep.py +600 -0
- attune/workflows/output.py +413 -0
- attune/workflows/perf_audit.py +863 -0
- attune/workflows/pr_review.py +762 -0
- attune/workflows/progress.py +785 -0
- attune/workflows/progress_server.py +322 -0
- attune/workflows/progressive/README 2.md +454 -0
- attune/workflows/progressive/README.md +454 -0
- attune/workflows/progressive/__init__.py +82 -0
- attune/workflows/progressive/cli.py +219 -0
- attune/workflows/progressive/core.py +488 -0
- attune/workflows/progressive/orchestrator.py +723 -0
- attune/workflows/progressive/reports.py +520 -0
- attune/workflows/progressive/telemetry.py +274 -0
- attune/workflows/progressive/test_gen.py +495 -0
- attune/workflows/progressive/workflow.py +589 -0
- attune/workflows/refactor_plan.py +694 -0
- attune/workflows/release_prep.py +895 -0
- attune/workflows/release_prep_crew.py +969 -0
- attune/workflows/research_synthesis.py +404 -0
- attune/workflows/routing.py +168 -0
- attune/workflows/secure_release.py +593 -0
- attune/workflows/security_adapters.py +297 -0
- attune/workflows/security_audit.py +1329 -0
- attune/workflows/security_audit_phase3.py +355 -0
- attune/workflows/seo_optimization.py +633 -0
- attune/workflows/step_config.py +234 -0
- attune/workflows/telemetry_mixin.py +269 -0
- attune/workflows/test5.py +125 -0
- attune/workflows/test5_README.md +158 -0
- attune/workflows/test_coverage_boost_crew.py +849 -0
- attune/workflows/test_gen/__init__.py +52 -0
- attune/workflows/test_gen/ast_analyzer.py +249 -0
- attune/workflows/test_gen/config.py +88 -0
- attune/workflows/test_gen/data_models.py +38 -0
- attune/workflows/test_gen/report_formatter.py +289 -0
- attune/workflows/test_gen/test_templates.py +381 -0
- attune/workflows/test_gen/workflow.py +655 -0
- attune/workflows/test_gen.py +54 -0
- attune/workflows/test_gen_behavioral.py +477 -0
- attune/workflows/test_gen_parallel.py +341 -0
- attune/workflows/test_lifecycle.py +526 -0
- attune/workflows/test_maintenance.py +627 -0
- attune/workflows/test_maintenance_cli.py +590 -0
- attune/workflows/test_maintenance_crew.py +840 -0
- attune/workflows/test_runner.py +622 -0
- attune/workflows/tier_tracking.py +531 -0
- attune/workflows/xml_enhanced_crew.py +285 -0
- attune_ai-2.0.0.dist-info/METADATA +1026 -0
- attune_ai-2.0.0.dist-info/RECORD +457 -0
- attune_ai-2.0.0.dist-info/WHEEL +5 -0
- attune_ai-2.0.0.dist-info/entry_points.txt +26 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE +201 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +101 -0
- attune_ai-2.0.0.dist-info/top_level.txt +5 -0
- attune_healthcare/__init__.py +13 -0
- attune_healthcare/monitors/__init__.py +9 -0
- attune_healthcare/monitors/clinical_protocol_monitor.py +315 -0
- attune_healthcare/monitors/monitoring/__init__.py +44 -0
- attune_healthcare/monitors/monitoring/protocol_checker.py +300 -0
- attune_healthcare/monitors/monitoring/protocol_loader.py +214 -0
- attune_healthcare/monitors/monitoring/sensor_parsers.py +306 -0
- attune_healthcare/monitors/monitoring/trajectory_analyzer.py +389 -0
- attune_llm/README.md +553 -0
- attune_llm/__init__.py +28 -0
- attune_llm/agent_factory/__init__.py +53 -0
- attune_llm/agent_factory/adapters/__init__.py +85 -0
- attune_llm/agent_factory/adapters/autogen_adapter.py +312 -0
- attune_llm/agent_factory/adapters/crewai_adapter.py +483 -0
- attune_llm/agent_factory/adapters/haystack_adapter.py +298 -0
- attune_llm/agent_factory/adapters/langchain_adapter.py +362 -0
- attune_llm/agent_factory/adapters/langgraph_adapter.py +333 -0
- attune_llm/agent_factory/adapters/native.py +228 -0
- attune_llm/agent_factory/adapters/wizard_adapter.py +423 -0
- attune_llm/agent_factory/base.py +305 -0
- attune_llm/agent_factory/crews/__init__.py +67 -0
- attune_llm/agent_factory/crews/code_review.py +1113 -0
- attune_llm/agent_factory/crews/health_check.py +1262 -0
- attune_llm/agent_factory/crews/refactoring.py +1128 -0
- attune_llm/agent_factory/crews/security_audit.py +1018 -0
- attune_llm/agent_factory/decorators.py +287 -0
- attune_llm/agent_factory/factory.py +558 -0
- attune_llm/agent_factory/framework.py +193 -0
- attune_llm/agent_factory/memory_integration.py +328 -0
- attune_llm/agent_factory/resilient.py +320 -0
- attune_llm/agents_md/__init__.py +22 -0
- attune_llm/agents_md/loader.py +218 -0
- attune_llm/agents_md/parser.py +271 -0
- attune_llm/agents_md/registry.py +307 -0
- attune_llm/claude_memory.py +466 -0
- attune_llm/cli/__init__.py +8 -0
- attune_llm/cli/sync_claude.py +487 -0
- attune_llm/code_health.py +1313 -0
- attune_llm/commands/__init__.py +51 -0
- attune_llm/commands/context.py +375 -0
- attune_llm/commands/loader.py +301 -0
- attune_llm/commands/models.py +231 -0
- attune_llm/commands/parser.py +371 -0
- attune_llm/commands/registry.py +429 -0
- attune_llm/config/__init__.py +29 -0
- attune_llm/config/unified.py +291 -0
- attune_llm/context/__init__.py +22 -0
- attune_llm/context/compaction.py +455 -0
- attune_llm/context/manager.py +434 -0
- attune_llm/contextual_patterns.py +361 -0
- attune_llm/core.py +907 -0
- attune_llm/git_pattern_extractor.py +435 -0
- attune_llm/hooks/__init__.py +24 -0
- attune_llm/hooks/config.py +306 -0
- attune_llm/hooks/executor.py +289 -0
- attune_llm/hooks/registry.py +302 -0
- attune_llm/hooks/scripts/__init__.py +39 -0
- attune_llm/hooks/scripts/evaluate_session.py +201 -0
- attune_llm/hooks/scripts/first_time_init.py +285 -0
- attune_llm/hooks/scripts/pre_compact.py +207 -0
- attune_llm/hooks/scripts/session_end.py +183 -0
- attune_llm/hooks/scripts/session_start.py +163 -0
- attune_llm/hooks/scripts/suggest_compact.py +225 -0
- attune_llm/learning/__init__.py +30 -0
- attune_llm/learning/evaluator.py +438 -0
- attune_llm/learning/extractor.py +514 -0
- attune_llm/learning/storage.py +560 -0
- attune_llm/levels.py +227 -0
- attune_llm/pattern_confidence.py +414 -0
- attune_llm/pattern_resolver.py +272 -0
- attune_llm/pattern_summary.py +350 -0
- attune_llm/providers.py +967 -0
- attune_llm/routing/__init__.py +32 -0
- attune_llm/routing/model_router.py +362 -0
- attune_llm/security/IMPLEMENTATION_SUMMARY.md +413 -0
- attune_llm/security/PHASE2_COMPLETE.md +384 -0
- attune_llm/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
- attune_llm/security/QUICK_REFERENCE.md +316 -0
- attune_llm/security/README.md +262 -0
- attune_llm/security/__init__.py +62 -0
- attune_llm/security/audit_logger.py +929 -0
- attune_llm/security/audit_logger_example.py +152 -0
- attune_llm/security/pii_scrubber.py +640 -0
- attune_llm/security/secrets_detector.py +678 -0
- attune_llm/security/secrets_detector_example.py +304 -0
- attune_llm/security/secure_memdocs.py +1192 -0
- attune_llm/security/secure_memdocs_example.py +278 -0
- attune_llm/session_status.py +745 -0
- attune_llm/state.py +246 -0
- attune_llm/utils/__init__.py +5 -0
- attune_llm/utils/tokens.py +349 -0
- attune_software/SOFTWARE_PLUGIN_README.md +57 -0
- attune_software/__init__.py +13 -0
- attune_software/cli/__init__.py +120 -0
- attune_software/cli/inspect.py +362 -0
- attune_software/cli.py +574 -0
- attune_software/plugin.py +188 -0
- workflow_scaffolding/__init__.py +11 -0
- workflow_scaffolding/__main__.py +12 -0
- workflow_scaffolding/cli.py +206 -0
- workflow_scaffolding/generator.py +265 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Risk-Driven Test Generator for Workflow Factory.
|
|
2
|
+
|
|
3
|
+
Generates comprehensive tests for workflows based on risk analysis and patterns.
|
|
4
|
+
|
|
5
|
+
Features:
|
|
6
|
+
- Risk-based test prioritization
|
|
7
|
+
- Pattern-driven test generation
|
|
8
|
+
- Jinja2 templates for unit/integration/E2E tests
|
|
9
|
+
- Fixture generation for common patterns
|
|
10
|
+
- CLI integration
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
from test_generator import TestGenerator
|
|
14
|
+
|
|
15
|
+
generator = TestGenerator()
|
|
16
|
+
tests = generator.generate_tests(
|
|
17
|
+
workflow_id="soap_note",
|
|
18
|
+
pattern_ids=["linear_flow", "approval", "structured_fields"]
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Write tests to file
|
|
22
|
+
with open("test_soap_note.py", "w") as f:
|
|
23
|
+
f.write(tests["unit"])
|
|
24
|
+
|
|
25
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
26
|
+
Licensed under Fair Source 0.9
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from .generator import TestGenerator
|
|
30
|
+
from .risk_analyzer import RiskAnalysis, RiskAnalyzer
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"TestGenerator",
|
|
34
|
+
"RiskAnalyzer",
|
|
35
|
+
"RiskAnalysis",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Make test_generator runnable as a module.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
python -m test_generator generate soap_note --patterns linear_flow,approval
|
|
5
|
+
python -m test_generator analyze debugging --patterns code_analysis_input
|
|
6
|
+
|
|
7
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
8
|
+
Licensed under Fair Source 0.9
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .cli import main
|
|
12
|
+
|
|
13
|
+
if __name__ == "__main__":
|
|
14
|
+
main()
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""CLI for test generator.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
python -m test_generator.cli generate soap_note --patterns linear_flow,approval,structured_fields
|
|
5
|
+
python -m test_generator.cli analyze soap_note --patterns linear_flow,approval
|
|
6
|
+
|
|
7
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
8
|
+
Licensed under Fair Source 0.9
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import heapq
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
from attune.config import _validate_file_path
|
|
19
|
+
|
|
20
|
+
from .generator import TestGenerator
|
|
21
|
+
from .risk_analyzer import RiskAnalyzer
|
|
22
|
+
|
|
23
|
+
logging.basicConfig(
|
|
24
|
+
level=logging.INFO,
|
|
25
|
+
format="%(levelname)s: %(message)s",
|
|
26
|
+
)
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def cmd_generate(args):
|
|
31
|
+
"""Generate tests for a workflow.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
args: Command line arguments
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
workflow_id = args.workflow_id
|
|
38
|
+
pattern_ids = args.patterns.split(",") if args.patterns else []
|
|
39
|
+
|
|
40
|
+
logger.info(f"Generating tests for workflow: {workflow_id}")
|
|
41
|
+
logger.info(f"Patterns: {', '.join(pattern_ids)}")
|
|
42
|
+
|
|
43
|
+
# Generate tests
|
|
44
|
+
generator = TestGenerator()
|
|
45
|
+
tests = generator.generate_tests(
|
|
46
|
+
workflow_id=workflow_id,
|
|
47
|
+
pattern_ids=pattern_ids,
|
|
48
|
+
workflow_module=args.module,
|
|
49
|
+
workflow_class=args.workflow_class,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Determine output directory
|
|
53
|
+
output_dir = Path(args.output) if args.output else Path("tests/unit/workflows")
|
|
54
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
55
|
+
|
|
56
|
+
# Write unit tests
|
|
57
|
+
unit_test_file = output_dir / f"test_{workflow_id}_workflow.py"
|
|
58
|
+
validated_unit_test = _validate_file_path(str(unit_test_file))
|
|
59
|
+
with open(validated_unit_test, "w") as f:
|
|
60
|
+
f.write(tests["unit"])
|
|
61
|
+
logger.info(f"✓ Unit tests written to: {validated_unit_test}")
|
|
62
|
+
|
|
63
|
+
# Write integration tests (if generated)
|
|
64
|
+
if tests["integration"]:
|
|
65
|
+
integration_test_file = (
|
|
66
|
+
output_dir.parent.parent / "integration" / f"test_{workflow_id}_integration.py"
|
|
67
|
+
)
|
|
68
|
+
integration_test_file.parent.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
validated_integration = _validate_file_path(str(integration_test_file))
|
|
70
|
+
with open(validated_integration, "w") as f:
|
|
71
|
+
f.write(tests["integration"])
|
|
72
|
+
logger.info(f"✓ Integration tests written to: {validated_integration}")
|
|
73
|
+
|
|
74
|
+
# Write fixtures
|
|
75
|
+
fixtures_file = output_dir / f"fixtures_{workflow_id}.py"
|
|
76
|
+
validated_fixtures = _validate_file_path(str(fixtures_file))
|
|
77
|
+
with open(validated_fixtures, "w") as f:
|
|
78
|
+
f.write(tests["fixtures"])
|
|
79
|
+
logger.info(f"✓ Fixtures written to: {validated_fixtures}")
|
|
80
|
+
|
|
81
|
+
print("\n🎉 Test generation complete!")
|
|
82
|
+
print("\nGenerated files:")
|
|
83
|
+
print(f" - {unit_test_file}")
|
|
84
|
+
if tests["integration"]:
|
|
85
|
+
print(f" - {integration_test_file}")
|
|
86
|
+
print(f" - {fixtures_file}")
|
|
87
|
+
|
|
88
|
+
print("\nNext steps:")
|
|
89
|
+
print(" 1. Review generated tests")
|
|
90
|
+
print(f" 2. Run tests: pytest {unit_test_file}")
|
|
91
|
+
print(" 3. Add custom test cases as needed")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def cmd_analyze(args):
|
|
95
|
+
"""Analyze workflow risk and show recommendations.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
args: Command line arguments
|
|
99
|
+
|
|
100
|
+
"""
|
|
101
|
+
workflow_id = args.workflow_id
|
|
102
|
+
pattern_ids = args.patterns.split(",") if args.patterns else []
|
|
103
|
+
|
|
104
|
+
logger.info(f"Analyzing workflow: {workflow_id}")
|
|
105
|
+
|
|
106
|
+
# Perform risk analysis
|
|
107
|
+
analyzer = RiskAnalyzer()
|
|
108
|
+
analysis = analyzer.analyze(workflow_id, pattern_ids)
|
|
109
|
+
|
|
110
|
+
# Display results
|
|
111
|
+
print(f"\n{'=' * 60}")
|
|
112
|
+
print(f"Risk Analysis: {workflow_id}")
|
|
113
|
+
print(f"{'=' * 60}\n")
|
|
114
|
+
|
|
115
|
+
print(f"Patterns Used: {len(analysis.pattern_ids)}")
|
|
116
|
+
for pid in analysis.pattern_ids:
|
|
117
|
+
print(f" - {pid}")
|
|
118
|
+
|
|
119
|
+
print(f"\nCritical Paths: {len(analysis.critical_paths)}")
|
|
120
|
+
for path in analysis.critical_paths:
|
|
121
|
+
print(f" - {path}")
|
|
122
|
+
|
|
123
|
+
print(f"\nHigh-Risk Inputs: {len(analysis.high_risk_inputs)}")
|
|
124
|
+
for input_risk in analysis.high_risk_inputs[:5]: # Top 5
|
|
125
|
+
print(f" - {input_risk}")
|
|
126
|
+
|
|
127
|
+
print(f"\nValidation Points: {len(analysis.validation_points)}")
|
|
128
|
+
for validation in analysis.validation_points[:5]: # Top 5
|
|
129
|
+
print(f" - {validation}")
|
|
130
|
+
|
|
131
|
+
print(f"\n{'=' * 60}")
|
|
132
|
+
print(f"Recommended Test Coverage: {analysis.recommended_coverage}%")
|
|
133
|
+
print(f"{'=' * 60}\n")
|
|
134
|
+
|
|
135
|
+
print("Test Priorities:")
|
|
136
|
+
for test_name, priority in heapq.nsmallest(
|
|
137
|
+
10, analysis.test_priorities.items(), key=lambda x: x[1]
|
|
138
|
+
):
|
|
139
|
+
priority_label = {
|
|
140
|
+
1: "CRITICAL",
|
|
141
|
+
2: "HIGH",
|
|
142
|
+
3: "MEDIUM",
|
|
143
|
+
4: "LOW",
|
|
144
|
+
5: "OPTIONAL",
|
|
145
|
+
}.get(priority, "UNKNOWN")
|
|
146
|
+
print(f" [{priority_label:8}] {test_name}")
|
|
147
|
+
|
|
148
|
+
# JSON output if requested
|
|
149
|
+
if args.json:
|
|
150
|
+
json_output = analysis.to_dict()
|
|
151
|
+
json_file = Path(f"{workflow_id}_risk_analysis.json")
|
|
152
|
+
with open(json_file, "w") as f:
|
|
153
|
+
json.dump(json_output, f, indent=2)
|
|
154
|
+
print(f"\n✓ JSON output written to: {json_file}")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def main():
|
|
158
|
+
"""Main CLI entry point."""
|
|
159
|
+
parser = argparse.ArgumentParser(
|
|
160
|
+
description="Test Generator for Empathy Workflow Factory",
|
|
161
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
162
|
+
epilog="""
|
|
163
|
+
Examples:
|
|
164
|
+
# Generate tests for SOAP Note workflow
|
|
165
|
+
%(prog)s generate soap_note --patterns linear_flow,approval,structured_fields
|
|
166
|
+
|
|
167
|
+
# Analyze risk for debugging workflow
|
|
168
|
+
%(prog)s analyze debugging --patterns code_analysis_input,risk_assessment
|
|
169
|
+
|
|
170
|
+
# Generate with custom module/class
|
|
171
|
+
%(prog)s generate my_workflow --patterns linear_flow --module workflows.my_workflow --class MyWorkflow
|
|
172
|
+
|
|
173
|
+
# Output to custom directory
|
|
174
|
+
%(prog)s generate soap_note --patterns linear_flow --output tests/custom/
|
|
175
|
+
""",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
179
|
+
|
|
180
|
+
# Generate command
|
|
181
|
+
gen_parser = subparsers.add_parser("generate", help="Generate tests for a workflow")
|
|
182
|
+
gen_parser.add_argument("workflow_id", help="Workflow identifier (e.g., soap_note)")
|
|
183
|
+
gen_parser.add_argument(
|
|
184
|
+
"--patterns",
|
|
185
|
+
required=True,
|
|
186
|
+
help="Comma-separated pattern IDs (e.g., linear_flow,approval)",
|
|
187
|
+
)
|
|
188
|
+
gen_parser.add_argument(
|
|
189
|
+
"--module",
|
|
190
|
+
help="Python module path (e.g., workflows.soap_note)",
|
|
191
|
+
)
|
|
192
|
+
gen_parser.add_argument(
|
|
193
|
+
"--class",
|
|
194
|
+
dest="workflow_class",
|
|
195
|
+
help="Workflow class name (e.g., SOAPNoteWorkflow)",
|
|
196
|
+
)
|
|
197
|
+
gen_parser.add_argument(
|
|
198
|
+
"--output",
|
|
199
|
+
"-o",
|
|
200
|
+
help="Output directory (default: tests/unit/workflows/)",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Analyze command
|
|
204
|
+
analyze_parser = subparsers.add_parser("analyze", help="Analyze workflow risk")
|
|
205
|
+
analyze_parser.add_argument("workflow_id", help="Workflow identifier")
|
|
206
|
+
analyze_parser.add_argument(
|
|
207
|
+
"--patterns",
|
|
208
|
+
required=True,
|
|
209
|
+
help="Comma-separated pattern IDs",
|
|
210
|
+
)
|
|
211
|
+
analyze_parser.add_argument(
|
|
212
|
+
"--json",
|
|
213
|
+
action="store_true",
|
|
214
|
+
help="Output JSON file with analysis results",
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
args = parser.parse_args()
|
|
218
|
+
|
|
219
|
+
if not args.command:
|
|
220
|
+
parser.print_help()
|
|
221
|
+
sys.exit(1)
|
|
222
|
+
|
|
223
|
+
# Execute command
|
|
224
|
+
if args.command == "generate":
|
|
225
|
+
cmd_generate(args)
|
|
226
|
+
elif args.command == "analyze":
|
|
227
|
+
cmd_analyze(args)
|
|
228
|
+
else:
|
|
229
|
+
parser.print_help()
|
|
230
|
+
sys.exit(1)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
if __name__ == "__main__":
|
|
234
|
+
main()
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"""Test generator core implementation.
|
|
2
|
+
|
|
3
|
+
Generates comprehensive tests for workflows using Jinja2 templates
|
|
4
|
+
and risk-based prioritization.
|
|
5
|
+
|
|
6
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
7
|
+
Licensed under Fair Source 0.9
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from jinja2 import Environment, FileSystemLoader
|
|
16
|
+
|
|
17
|
+
from patterns import get_pattern_registry
|
|
18
|
+
from patterns.structural import LinearFlowPattern, PhasedProcessingPattern
|
|
19
|
+
|
|
20
|
+
from .risk_analyzer import RiskAnalyzer
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_default_template_dir() -> Path:
|
|
26
|
+
"""Get default template directory.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Path to templates directory
|
|
30
|
+
|
|
31
|
+
Note:
|
|
32
|
+
Uses multiple fallback strategies to avoid __file__ AttributeError
|
|
33
|
+
in pytest environments.
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
# Try __file__ first (works in most cases)
|
|
37
|
+
return Path(__file__).parent / "templates"
|
|
38
|
+
except (AttributeError, NameError):
|
|
39
|
+
# Fallback: Use module __spec__ if available
|
|
40
|
+
import importlib.util
|
|
41
|
+
import sys
|
|
42
|
+
|
|
43
|
+
spec = importlib.util.find_spec("attune.test_generator")
|
|
44
|
+
if spec and spec.origin:
|
|
45
|
+
return Path(spec.origin).parent / "templates"
|
|
46
|
+
|
|
47
|
+
# Last resort: Try to find from sys.modules
|
|
48
|
+
this_module = sys.modules.get(__name__)
|
|
49
|
+
if this_module and hasattr(this_module, "__file__") and this_module.__file__:
|
|
50
|
+
return Path(this_module.__file__).parent / "templates"
|
|
51
|
+
|
|
52
|
+
# Final fallback: Use current working directory
|
|
53
|
+
return Path.cwd() / "src" / "attune" / "test_generator" / "templates"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TestGenerator:
|
|
57
|
+
"""Generates tests for workflows based on patterns and risk analysis.
|
|
58
|
+
|
|
59
|
+
Uses Jinja2 templates to generate:
|
|
60
|
+
- Unit tests with risk-prioritized coverage
|
|
61
|
+
- Integration tests for multi-step workflows
|
|
62
|
+
- E2E tests for critical paths
|
|
63
|
+
- Test fixtures for common patterns
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(self, template_dir: Path | None = None):
|
|
67
|
+
"""Initialize test generator.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
template_dir: Directory containing Jinja2 templates
|
|
71
|
+
(defaults to test_generator/templates/)
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
if template_dir is None:
|
|
75
|
+
template_dir = _get_default_template_dir()
|
|
76
|
+
|
|
77
|
+
self.template_dir = template_dir
|
|
78
|
+
self.env = Environment(
|
|
79
|
+
loader=FileSystemLoader(str(template_dir)),
|
|
80
|
+
trim_blocks=True,
|
|
81
|
+
lstrip_blocks=True,
|
|
82
|
+
autoescape=True, # Enable autoescape for security (test gen templates should be safe)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
self.risk_analyzer = RiskAnalyzer()
|
|
86
|
+
self.registry = get_pattern_registry()
|
|
87
|
+
|
|
88
|
+
def generate_tests(
|
|
89
|
+
self,
|
|
90
|
+
workflow_id: str,
|
|
91
|
+
pattern_ids: list[str],
|
|
92
|
+
workflow_module: str | None = None,
|
|
93
|
+
workflow_class: str | None = None,
|
|
94
|
+
) -> dict[str, str | None]:
|
|
95
|
+
"""Generate tests for a workflow.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
workflow_id: Workflow identifier
|
|
99
|
+
pattern_ids: List of pattern IDs used by workflow
|
|
100
|
+
workflow_module: Python module path (e.g., "workflows.soap_note")
|
|
101
|
+
workflow_class: Workflow class name (e.g., "SOAPNoteWorkflow")
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Dictionary with test types:
|
|
105
|
+
{
|
|
106
|
+
"unit": "...", # Unit test code
|
|
107
|
+
"integration": "...", # Integration test code (if applicable)
|
|
108
|
+
"fixtures": "...", # Test fixtures
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
logger.info(f"Generating tests for workflow: {workflow_id}")
|
|
113
|
+
|
|
114
|
+
# Perform risk analysis
|
|
115
|
+
risk_analysis = self.risk_analyzer.analyze(workflow_id, pattern_ids)
|
|
116
|
+
|
|
117
|
+
# Infer module and class if not provided
|
|
118
|
+
if not workflow_module:
|
|
119
|
+
workflow_module = self._infer_module(workflow_id)
|
|
120
|
+
|
|
121
|
+
if not workflow_class:
|
|
122
|
+
workflow_class = self._infer_class_name(workflow_id)
|
|
123
|
+
|
|
124
|
+
# Gather template context
|
|
125
|
+
context = self._build_template_context(
|
|
126
|
+
workflow_id=workflow_id,
|
|
127
|
+
pattern_ids=pattern_ids,
|
|
128
|
+
workflow_module=workflow_module,
|
|
129
|
+
workflow_class=workflow_class,
|
|
130
|
+
risk_analysis=risk_analysis,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Generate unit tests (always)
|
|
134
|
+
unit_tests = self._generate_unit_tests(context)
|
|
135
|
+
|
|
136
|
+
# Generate integration tests (for multi-step workflows)
|
|
137
|
+
integration_tests = None
|
|
138
|
+
if self._needs_integration_tests(pattern_ids):
|
|
139
|
+
integration_tests = self._generate_integration_tests(context)
|
|
140
|
+
|
|
141
|
+
# Generate test fixtures
|
|
142
|
+
fixtures = self._generate_fixtures(context)
|
|
143
|
+
|
|
144
|
+
logger.info(
|
|
145
|
+
f"Generated tests for {workflow_id}: "
|
|
146
|
+
f"unit={len(unit_tests)} chars, "
|
|
147
|
+
f"integration={'yes' if integration_tests else 'no'}, "
|
|
148
|
+
f"fixtures={len(fixtures)} chars"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
"unit": unit_tests,
|
|
153
|
+
"integration": integration_tests,
|
|
154
|
+
"fixtures": fixtures,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
def _build_template_context(
|
|
158
|
+
self,
|
|
159
|
+
workflow_id: str,
|
|
160
|
+
pattern_ids: list[str],
|
|
161
|
+
workflow_module: str,
|
|
162
|
+
workflow_class: str,
|
|
163
|
+
risk_analysis: Any,
|
|
164
|
+
) -> dict:
|
|
165
|
+
"""Build Jinja2 template context.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
workflow_id: Workflow identifier
|
|
169
|
+
pattern_ids: Pattern IDs
|
|
170
|
+
workflow_module: Module path
|
|
171
|
+
workflow_class: Class name
|
|
172
|
+
risk_analysis: RiskAnalysis object
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Context dictionary for templates
|
|
176
|
+
|
|
177
|
+
"""
|
|
178
|
+
# Get pattern details
|
|
179
|
+
patterns = [self.registry.get(pid) for pid in pattern_ids if self.registry.get(pid)]
|
|
180
|
+
|
|
181
|
+
# Check for specific patterns
|
|
182
|
+
has_linear_flow = "linear_flow" in pattern_ids
|
|
183
|
+
has_phased = "phased_processing" in pattern_ids
|
|
184
|
+
has_approval = "approval" in pattern_ids
|
|
185
|
+
has_async = True # Assume async by default for modern workflows
|
|
186
|
+
|
|
187
|
+
# Get linear flow details if present
|
|
188
|
+
total_steps = None
|
|
189
|
+
if has_linear_flow:
|
|
190
|
+
linear_pattern = self.registry.get("linear_flow")
|
|
191
|
+
if isinstance(linear_pattern, LinearFlowPattern):
|
|
192
|
+
total_steps = linear_pattern.total_steps
|
|
193
|
+
|
|
194
|
+
# Get phases if present
|
|
195
|
+
phases = []
|
|
196
|
+
if has_phased:
|
|
197
|
+
phased_pattern = self.registry.get("phased_processing")
|
|
198
|
+
if isinstance(phased_pattern, PhasedProcessingPattern):
|
|
199
|
+
phases = phased_pattern.phases
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
"workflow_id": workflow_id,
|
|
203
|
+
"workflow_module": workflow_module,
|
|
204
|
+
"workflow_class": workflow_class,
|
|
205
|
+
"pattern_ids": pattern_ids,
|
|
206
|
+
"patterns": patterns,
|
|
207
|
+
"risk_analysis": risk_analysis,
|
|
208
|
+
"has_async": has_async,
|
|
209
|
+
"has_linear_flow": has_linear_flow,
|
|
210
|
+
"has_phased": has_phased,
|
|
211
|
+
"has_approval": has_approval,
|
|
212
|
+
"total_steps": total_steps,
|
|
213
|
+
"phases": phases,
|
|
214
|
+
"timestamp": datetime.now().isoformat(),
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
def _generate_unit_tests(self, context: dict) -> str:
|
|
218
|
+
"""Generate unit tests from template.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
context: Template context
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Generated unit test code
|
|
225
|
+
|
|
226
|
+
"""
|
|
227
|
+
template = self.env.get_template("unit_test.py.jinja2")
|
|
228
|
+
return template.render(**context)
|
|
229
|
+
|
|
230
|
+
def _generate_integration_tests(self, context: dict) -> str:
|
|
231
|
+
"""Generate integration tests.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
context: Template context
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
Generated integration test code
|
|
238
|
+
|
|
239
|
+
"""
|
|
240
|
+
# For now, return a simple integration test template
|
|
241
|
+
# In full implementation, would use integration_test.py.jinja2
|
|
242
|
+
return f'''"""Integration tests for {context["workflow_id"]} workflow.
|
|
243
|
+
|
|
244
|
+
Auto-generated by Empathy Framework Test Generator
|
|
245
|
+
Tests end-to-end workflow workflows.
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
import pytest
|
|
249
|
+
from {context["workflow_module"]} import {context["workflow_class"]}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class TestIntegration{context["workflow_class"]}:
|
|
253
|
+
"""Integration tests for {context["workflow_id"]} workflow."""
|
|
254
|
+
|
|
255
|
+
@pytest.fixture
|
|
256
|
+
async def workflow(self):
|
|
257
|
+
"""Create workflow instance."""
|
|
258
|
+
return {context["workflow_class"]}()
|
|
259
|
+
|
|
260
|
+
@pytest.mark.asyncio
|
|
261
|
+
async def test_complete_workflow_workflow(self, workflow):
|
|
262
|
+
"""Test complete workflow workflow end-to-end."""
|
|
263
|
+
# Start workflow
|
|
264
|
+
session = await workflow.start()
|
|
265
|
+
workflow_id = session["workflow_id"]
|
|
266
|
+
|
|
267
|
+
# Complete all steps with valid data
|
|
268
|
+
# TODO: Add step completion logic
|
|
269
|
+
|
|
270
|
+
# Verify final state
|
|
271
|
+
assert session.get("completed", False) is True
|
|
272
|
+
'''
|
|
273
|
+
|
|
274
|
+
def _generate_fixtures(self, context: dict) -> str:
|
|
275
|
+
"""Generate test fixtures.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
context: Template context
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
Generated fixture code
|
|
282
|
+
|
|
283
|
+
"""
|
|
284
|
+
return f'''"""Test fixtures for {context["workflow_id"]} workflow.
|
|
285
|
+
|
|
286
|
+
Auto-generated by Empathy Framework Test Generator
|
|
287
|
+
Provides common test data and mocks.
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
import pytest
|
|
291
|
+
from unittest.mock import MagicMock
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
@pytest.fixture
|
|
295
|
+
def sample_{context["workflow_id"]}_data():
|
|
296
|
+
"""Sample data for {context["workflow_id"]} workflow."""
|
|
297
|
+
return {{
|
|
298
|
+
"field1": "test value 1",
|
|
299
|
+
"field2": "test value 2",
|
|
300
|
+
}}
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
@pytest.fixture
|
|
304
|
+
def mock_{context["workflow_id"]}_dependencies():
|
|
305
|
+
"""Mock dependencies for {context["workflow_id"]} workflow."""
|
|
306
|
+
return {{
|
|
307
|
+
"database": MagicMock(),
|
|
308
|
+
"api_client": MagicMock(),
|
|
309
|
+
}}
|
|
310
|
+
'''
|
|
311
|
+
|
|
312
|
+
def _needs_integration_tests(self, pattern_ids: list[str]) -> bool:
|
|
313
|
+
"""Determine if integration tests are needed.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
pattern_ids: Pattern IDs
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
True if integration tests recommended
|
|
320
|
+
|
|
321
|
+
"""
|
|
322
|
+
# Integration tests for multi-step or phased workflows
|
|
323
|
+
return "linear_flow" in pattern_ids or "phased_processing" in pattern_ids
|
|
324
|
+
|
|
325
|
+
def _infer_module(self, workflow_id: str) -> str:
|
|
326
|
+
"""Infer module path from workflow ID.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
workflow_id: Workflow identifier
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
Inferred module path
|
|
333
|
+
|
|
334
|
+
"""
|
|
335
|
+
# Try to determine workflow location
|
|
336
|
+
# This is a simple heuristic - can be improved
|
|
337
|
+
if workflow_id in ["soap_note", "sbar", "care_plan"]:
|
|
338
|
+
return f"workflows.{workflow_id}"
|
|
339
|
+
else:
|
|
340
|
+
return f"workflows.{workflow_id}_workflow"
|
|
341
|
+
|
|
342
|
+
def _infer_class_name(self, workflow_id: str) -> str:
|
|
343
|
+
"""Infer workflow class name from ID.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
workflow_id: Workflow identifier
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
Inferred class name
|
|
350
|
+
|
|
351
|
+
"""
|
|
352
|
+
# Convert snake_case to PascalCase and add "Workflow"
|
|
353
|
+
parts = workflow_id.split("_")
|
|
354
|
+
class_name = "".join(part.capitalize() for part in parts)
|
|
355
|
+
return f"{class_name}Workflow"
|