ci / test (push) Successful in 2m12s
- Add RoutingWorkflow: generic state machine executor for WorkflowSpec - Add LLM Router: natural language → WorkflowSpec generation - Add RetrieveMemoryActivity: query poimen-memory for context - Add activities: AnalyzeCode, SecurityScan, GenerateReport, Notify, etc. - Add agent-prompts/router: LLM prompt documentation - Extend starter with --route flag for routing workflows - Remove orchestrator job (trigger via API/message instead) - Clean up: move docs to Desktop, add .gitignore for *.md
146 lines
4.4 KiB
Go
146 lines
4.4 KiB
Go
// +build integration
|
|
|
|
package tests
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rockliang/poimen/workflows/internal/routing"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestRoutingE2E_GenerateAndValidate tests full flow against real api.riotpiao.com
|
|
// Run with: go test -tags=integration -v -run TestRoutingE2E ./tests/...
|
|
func TestRoutingE2E_GenerateAndValidate(t *testing.T) {
|
|
if os.Getenv("RUN_INTEGRATION_TESTS") != "1" {
|
|
t.Skip("Skipping integration test. Set RUN_INTEGRATION_TESTS=1 to run.")
|
|
}
|
|
|
|
// Load knowledge base
|
|
kb, err := routing.LoadKnowledgeBaseFromDefaultPath()
|
|
require.NoError(t, err, "failed to load knowledge base")
|
|
|
|
// Create router
|
|
router, err := routing.NewLLMRouter(kb)
|
|
require.NoError(t, err, "failed to create router")
|
|
|
|
// Create validator
|
|
validator := routing.NewValidator(kb)
|
|
|
|
tests := []struct {
|
|
name string
|
|
message string
|
|
context map[string]interface{}
|
|
isCron bool
|
|
}{
|
|
{
|
|
name: "one-time repo analysis",
|
|
message: "Analyze https://github.com/rockliang/poimen for code quality and security issues",
|
|
context: map[string]interface{}{"branch": "main"},
|
|
isCron: false,
|
|
},
|
|
{
|
|
name: "scheduled security scan",
|
|
message: "Run daily security scan at 3 AM on https://github.com/rockliang/poimen",
|
|
isCron: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
defer cancel()
|
|
|
|
// Generate workflow spec
|
|
input := routing.LLMRouterInput{
|
|
Message: tt.message,
|
|
Context: tt.context,
|
|
}
|
|
|
|
output, err := router.Route(ctx, input)
|
|
require.NoError(t, err, "LLM router failed")
|
|
|
|
// Log generated spec
|
|
specJSON, _ := json.MarshalIndent(output, "", " ")
|
|
t.Logf("Generated spec:\n%s", string(specJSON))
|
|
|
|
// Validate based on type
|
|
if tt.isCron {
|
|
require.True(t, output.IsCron, "expected cron workflow")
|
|
require.NotNil(t, output.CronSpec, "cron spec is nil")
|
|
require.NotEmpty(t, output.CronSpec.Schedule, "cron schedule is empty")
|
|
|
|
result := validator.ValidateCronWorkflowSpec(output.CronSpec)
|
|
require.True(t, result.Valid, "validation failed: %v", result.Errors)
|
|
|
|
t.Logf("Cron workflow validated: %s (schedule: %s)",
|
|
output.CronSpec.Name, output.CronSpec.Schedule)
|
|
} else {
|
|
require.False(t, output.IsCron, "expected one-time workflow")
|
|
require.NotNil(t, output.Spec, "spec is nil")
|
|
|
|
result := validator.ValidateWorkflowSpec(output.Spec)
|
|
require.True(t, result.Valid, "validation failed: %v", result.Errors)
|
|
|
|
t.Logf("One-time workflow validated: %s (%d states)",
|
|
output.Spec.Name, len(output.Spec.States))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestRoutingE2E_FullPipeline tests LLM router -> validation -> (simulated) execution
|
|
func TestRoutingE2E_FullPipeline(t *testing.T) {
|
|
if os.Getenv("RUN_INTEGRATION_TESTS") != "1" {
|
|
t.Skip("Skipping integration test. Set RUN_INTEGRATION_TESTS=1 to run.")
|
|
}
|
|
|
|
kb, err := routing.LoadKnowledgeBaseFromDefaultPath()
|
|
require.NoError(t, err)
|
|
|
|
router, err := routing.NewLLMRouter(kb)
|
|
require.NoError(t, err)
|
|
|
|
validator := routing.NewValidator(kb)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
|
|
defer cancel()
|
|
|
|
// Generate workflow
|
|
output, err := router.Route(ctx, routing.LLMRouterInput{
|
|
Message: "Clone and analyze https://github.com/rockliang/poimen for security vulnerabilities",
|
|
})
|
|
require.NoError(t, err)
|
|
require.False(t, output.IsCron)
|
|
require.NotNil(t, output.Spec)
|
|
|
|
// Validate
|
|
result := validator.ValidateWorkflowSpec(output.Spec)
|
|
require.True(t, result.Valid, "validation failed: %v", result.Errors)
|
|
|
|
// Verify structure
|
|
require.NotEmpty(t, output.Spec.Name)
|
|
require.NotEmpty(t, output.Spec.States)
|
|
|
|
// First state should be CloneRepoActivity
|
|
require.Equal(t, "CloneRepoActivity", output.Spec.States[0].Resource,
|
|
"expected first activity to be CloneRepoActivity")
|
|
|
|
// Check that flaky activities have retry policies
|
|
for _, state := range output.Spec.States {
|
|
if state.Type == routing.StateTypeTask {
|
|
if kb.IsFlaky(state.Resource) {
|
|
require.NotNil(t, state.Retry, "flaky activity %s should have retry policy", state.Resource)
|
|
require.GreaterOrEqual(t, state.Retry.MaxAttempts, int32(2),
|
|
"flaky activity %s should have at least 2 retries", state.Resource)
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Logf("Full pipeline test passed: %s with %d states", output.Spec.Name, len(output.Spec.States))
|
|
}
|