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
142 lines
4.2 KiB
Go
142 lines
4.2 KiB
Go
// +build integration
|
|
|
|
package tests
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rockliang/poimen/workflows/internal/routing"
|
|
"github.com/rockliang/poimen/workflows/statemachine"
|
|
"github.com/stretchr/testify/require"
|
|
"go.temporal.io/sdk/client"
|
|
)
|
|
|
|
// TestTemporalRoutingWorkflow tests full flow against real Temporal cluster
|
|
// Run with: TEMPORAL_HOSTPORT=temporal.riotpiao.com:7233 RUN_INTEGRATION_TESTS=1 go test -tags=integration -v -run TestTemporalRoutingWorkflow ./tests/...
|
|
func TestTemporalRoutingWorkflow(t *testing.T) {
|
|
if os.Getenv("RUN_INTEGRATION_TESTS") != "1" {
|
|
t.Skip("Skipping integration test. Set RUN_INTEGRATION_TESTS=1 to run.")
|
|
}
|
|
|
|
hostPort := os.Getenv("TEMPORAL_HOSTPORT")
|
|
if hostPort == "" {
|
|
hostPort = "temporal.riotpiao.com:7233"
|
|
}
|
|
|
|
namespace := os.Getenv("TEMPORAL_NAMESPACE")
|
|
if namespace == "" {
|
|
namespace = "poimen-harness"
|
|
}
|
|
|
|
t.Logf("Connecting to Temporal at %s (namespace: %s)", hostPort, namespace)
|
|
|
|
// Connect to Temporal
|
|
c, err := client.Dial(client.Options{
|
|
HostPort: hostPort,
|
|
Namespace: namespace,
|
|
})
|
|
if err != nil {
|
|
t.Skipf("Skipping - cannot connect to Temporal: %v", err)
|
|
}
|
|
defer c.Close()
|
|
|
|
t.Log("Connected to Temporal successfully")
|
|
|
|
// Test 1: Generate spec via LLM and submit
|
|
t.Run("LLM_Route_And_Submit", func(t *testing.T) {
|
|
// Load KB and create router
|
|
kb, err := routing.LoadKnowledgeBaseFromDefaultPath()
|
|
require.NoError(t, err)
|
|
|
|
router, err := routing.NewLLMRouter(kb)
|
|
require.NoError(t, err)
|
|
|
|
// Generate workflow spec
|
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
defer cancel()
|
|
|
|
output, err := router.Route(ctx, routing.LLMRouterInput{
|
|
Message: "Clone and analyze https://github.com/rockliang/poimen",
|
|
})
|
|
require.NoError(t, err)
|
|
require.False(t, output.IsCron)
|
|
require.NotNil(t, output.Spec)
|
|
|
|
t.Logf("Generated spec: %s with %d states", output.Spec.Name, len(output.Spec.States))
|
|
|
|
// Submit to Temporal
|
|
workflowID := "test-routing-" + time.Now().Format("20060102-150405")
|
|
input := statemachine.RoutingWorkflowInput{Spec: output.Spec}
|
|
|
|
run, err := c.ExecuteWorkflow(ctx, client.StartWorkflowOptions{
|
|
ID: workflowID,
|
|
TaskQueue: "poimen-taskqueue",
|
|
}, statemachine.RoutingWorkflow, input)
|
|
require.NoError(t, err)
|
|
|
|
t.Logf("Workflow submitted: ID=%s, RunID=%s", run.GetID(), run.GetRunID())
|
|
|
|
// Check workflow started (don't wait for completion - activities may not be registered)
|
|
desc, err := c.DescribeWorkflowExecution(ctx, workflowID, "")
|
|
require.NoError(t, err)
|
|
t.Logf("Workflow status: %s", desc.WorkflowExecutionInfo.Status.String())
|
|
|
|
// Cancel the workflow (since activities may not be running)
|
|
err = c.CancelWorkflow(ctx, workflowID, "")
|
|
if err != nil {
|
|
t.Logf("Cancel failed (may already be done): %v", err)
|
|
} else {
|
|
t.Log("Workflow cancelled")
|
|
}
|
|
})
|
|
|
|
// Test 2: Submit simple Pass-only workflow (no activities needed)
|
|
t.Run("PassOnly_Workflow", func(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
spec := &routing.WorkflowSpec{
|
|
Name: "pass-only-test",
|
|
Input: map[string]interface{}{"test": true},
|
|
States: []routing.State{
|
|
{
|
|
Name: "Step1",
|
|
Type: routing.StateTypePass,
|
|
Result: map[string]interface{}{"status": "step1-done"},
|
|
Next: "Step2",
|
|
},
|
|
{
|
|
Name: "Step2",
|
|
Type: routing.StateTypePass,
|
|
Result: map[string]interface{}{"status": "step2-done", "final": true},
|
|
End: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
workflowID := "test-pass-only-" + time.Now().Format("20060102-150405")
|
|
input := statemachine.RoutingWorkflowInput{Spec: spec}
|
|
|
|
run, err := c.ExecuteWorkflow(ctx, client.StartWorkflowOptions{
|
|
ID: workflowID,
|
|
TaskQueue: "poimen-taskqueue",
|
|
}, statemachine.RoutingWorkflow, input)
|
|
require.NoError(t, err)
|
|
|
|
t.Logf("Pass-only workflow submitted: ID=%s", run.GetID())
|
|
|
|
// Wait for result (Pass states don't need workers)
|
|
var result statemachine.RoutingWorkflowOutput
|
|
err = run.Get(ctx, &result)
|
|
require.NoError(t, err)
|
|
|
|
t.Logf("Workflow result: status=%s", result.Status)
|
|
require.Equal(t, "COMPLETED", result.Status)
|
|
require.Contains(t, result.StepResults, "Step1")
|
|
require.Contains(t, result.StepResults, "Step2")
|
|
})
|
|
}
|