// +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") }) }