416 lines
11 KiB
Go
416 lines
11 KiB
Go
//go:build integration
|
|
// +build integration
|
|
|
|
package client_test
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/gregor/prefect-go/pkg/client"
|
|
"github.com/gregor/prefect-go/pkg/errors"
|
|
"github.com/gregor/prefect-go/pkg/models"
|
|
)
|
|
|
|
var testClient *client.Client
|
|
|
|
func TestMain(m *testing.M) {
|
|
// Get base URL from environment or use default
|
|
baseURL := os.Getenv("PREFECT_API_URL")
|
|
if baseURL == "" {
|
|
baseURL = "http://localhost:4200/api"
|
|
}
|
|
|
|
// Create test client
|
|
var err error
|
|
testClient, err = client.NewClient(
|
|
client.WithBaseURL(baseURL),
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Run tests
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
func TestIntegration_FlowLifecycle(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Create a flow
|
|
flow, err := testClient.Flows.Create(ctx, &models.FlowCreate{
|
|
Name: "test-flow-" + uuid.New().String(),
|
|
Tags: []string{"integration-test"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to create flow: %v", err)
|
|
}
|
|
defer testClient.Flows.Delete(ctx, flow.ID)
|
|
|
|
// Verify flow was created
|
|
if flow.ID == uuid.Nil {
|
|
t.Error("Flow ID is nil")
|
|
}
|
|
if flow.Name == "" {
|
|
t.Error("Flow name is empty")
|
|
}
|
|
|
|
// Get the flow
|
|
retrievedFlow, err := testClient.Flows.Get(ctx, flow.ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get flow: %v", err)
|
|
}
|
|
if retrievedFlow.ID != flow.ID {
|
|
t.Errorf("Flow ID mismatch: got %v, want %v", retrievedFlow.ID, flow.ID)
|
|
}
|
|
|
|
// Update the flow
|
|
newTags := []string{"integration-test", "updated"}
|
|
updatedFlow, err := testClient.Flows.Update(ctx, flow.ID, &models.FlowUpdate{
|
|
Tags: &newTags,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to update flow: %v", err)
|
|
}
|
|
if len(updatedFlow.Tags) != 2 {
|
|
t.Errorf("Expected 2 tags, got %d", len(updatedFlow.Tags))
|
|
}
|
|
|
|
// List flows
|
|
flowsPage, err := testClient.Flows.List(ctx, &models.FlowFilter{
|
|
Tags: []string{"integration-test"},
|
|
}, 0, 10)
|
|
if err != nil {
|
|
t.Fatalf("Failed to list flows: %v", err)
|
|
}
|
|
if len(flowsPage.Results) == 0 {
|
|
t.Error("Expected at least one flow in results")
|
|
}
|
|
|
|
// Delete the flow
|
|
if err := testClient.Flows.Delete(ctx, flow.ID); err != nil {
|
|
t.Fatalf("Failed to delete flow: %v", err)
|
|
}
|
|
|
|
// Verify deletion
|
|
_, err = testClient.Flows.Get(ctx, flow.ID)
|
|
if !errors.IsNotFound(err) {
|
|
t.Errorf("Expected NotFound error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestIntegration_FlowRunLifecycle(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Create a flow first
|
|
flow, err := testClient.Flows.Create(ctx, &models.FlowCreate{
|
|
Name: "test-flow-run-" + uuid.New().String(),
|
|
Tags: []string{"integration-test"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to create flow: %v", err)
|
|
}
|
|
defer testClient.Flows.Delete(ctx, flow.ID)
|
|
|
|
// Create a flow run
|
|
flowRun, err := testClient.FlowRuns.Create(ctx, &models.FlowRunCreate{
|
|
FlowID: flow.ID,
|
|
Name: "test-run",
|
|
Parameters: map[string]interface{}{
|
|
"test_param": "value",
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to create flow run: %v", err)
|
|
}
|
|
defer testClient.FlowRuns.Delete(ctx, flowRun.ID)
|
|
|
|
// Verify flow run was created
|
|
if flowRun.ID == uuid.Nil {
|
|
t.Error("Flow run ID is nil")
|
|
}
|
|
if flowRun.FlowID != flow.ID {
|
|
t.Errorf("Flow ID mismatch: got %v, want %v", flowRun.FlowID, flow.ID)
|
|
}
|
|
|
|
// Get the flow run
|
|
retrievedRun, err := testClient.FlowRuns.Get(ctx, flowRun.ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get flow run: %v", err)
|
|
}
|
|
if retrievedRun.ID != flowRun.ID {
|
|
t.Errorf("Flow run ID mismatch: got %v, want %v", retrievedRun.ID, flowRun.ID)
|
|
}
|
|
|
|
// Set state to RUNNING
|
|
runningState := models.StateTypeRunning
|
|
updatedRun, err := testClient.FlowRuns.SetState(ctx, flowRun.ID, &models.StateCreate{
|
|
Type: runningState,
|
|
Message: strPtr("Test running"),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to set state: %v", err)
|
|
}
|
|
if updatedRun.StateType == nil || *updatedRun.StateType != runningState {
|
|
t.Errorf("State type mismatch: got %v, want %v", updatedRun.StateType, runningState)
|
|
}
|
|
|
|
// Set state to COMPLETED
|
|
completedState := models.StateTypeCompleted
|
|
completedRun, err := testClient.FlowRuns.SetState(ctx, flowRun.ID, &models.StateCreate{
|
|
Type: completedState,
|
|
Message: strPtr("Test completed"),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to set state: %v", err)
|
|
}
|
|
if completedRun.StateType == nil || *completedRun.StateType != completedState {
|
|
t.Errorf("State type mismatch: got %v, want %v", completedRun.StateType, completedState)
|
|
}
|
|
|
|
// List flow runs
|
|
runsPage, err := testClient.FlowRuns.List(ctx, &models.FlowRunFilter{
|
|
FlowID: &flow.ID,
|
|
}, 0, 10)
|
|
if err != nil {
|
|
t.Fatalf("Failed to list flow runs: %v", err)
|
|
}
|
|
if len(runsPage.Results) == 0 {
|
|
t.Error("Expected at least one flow run in results")
|
|
}
|
|
}
|
|
|
|
func TestIntegration_DeploymentLifecycle(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Create a flow first
|
|
flow, err := testClient.Flows.Create(ctx, &models.FlowCreate{
|
|
Name: "test-deployment-flow-" + uuid.New().String(),
|
|
Tags: []string{"integration-test"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to create flow: %v", err)
|
|
}
|
|
defer testClient.Flows.Delete(ctx, flow.ID)
|
|
|
|
// Create a deployment
|
|
workPoolName := "default-pool"
|
|
deployment, err := testClient.Deployments.Create(ctx, &models.DeploymentCreate{
|
|
Name: "test-deployment",
|
|
FlowID: flow.ID,
|
|
WorkPoolName: &workPoolName,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to create deployment: %v", err)
|
|
}
|
|
defer testClient.Deployments.Delete(ctx, deployment.ID)
|
|
|
|
// Verify deployment was created
|
|
if deployment.ID == uuid.Nil {
|
|
t.Error("Deployment ID is nil")
|
|
}
|
|
if deployment.FlowID != flow.ID {
|
|
t.Errorf("Flow ID mismatch: got %v, want %v", deployment.FlowID, flow.ID)
|
|
}
|
|
|
|
// Get the deployment
|
|
retrievedDeployment, err := testClient.Deployments.Get(ctx, deployment.ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get deployment: %v", err)
|
|
}
|
|
if retrievedDeployment.ID != deployment.ID {
|
|
t.Errorf("Deployment ID mismatch: got %v, want %v", retrievedDeployment.ID, deployment.ID)
|
|
}
|
|
|
|
// Pause the deployment
|
|
if err := testClient.Deployments.Pause(ctx, deployment.ID); err != nil {
|
|
t.Fatalf("Failed to pause deployment: %v", err)
|
|
}
|
|
|
|
// Verify paused
|
|
pausedDeployment, err := testClient.Deployments.Get(ctx, deployment.ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get deployment: %v", err)
|
|
}
|
|
if !pausedDeployment.Paused {
|
|
t.Error("Expected deployment to be paused")
|
|
}
|
|
|
|
// Resume the deployment
|
|
if err := testClient.Deployments.Resume(ctx, deployment.ID); err != nil {
|
|
t.Fatalf("Failed to resume deployment: %v", err)
|
|
}
|
|
|
|
// Verify resumed
|
|
resumedDeployment, err := testClient.Deployments.Get(ctx, deployment.ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get deployment: %v", err)
|
|
}
|
|
if resumedDeployment.Paused {
|
|
t.Error("Expected deployment to be resumed")
|
|
}
|
|
}
|
|
|
|
func TestIntegration_Pagination(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Create multiple flows
|
|
createdFlows := make([]uuid.UUID, 0)
|
|
for i := 0; i < 15; i++ {
|
|
flow, err := testClient.Flows.Create(ctx, &models.FlowCreate{
|
|
Name: "pagination-test-" + uuid.New().String(),
|
|
Tags: []string{"pagination-integration-test"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to create flow %d: %v", i, err)
|
|
}
|
|
createdFlows = append(createdFlows, flow.ID)
|
|
}
|
|
|
|
// Clean up
|
|
defer func() {
|
|
for _, id := range createdFlows {
|
|
testClient.Flows.Delete(ctx, id)
|
|
}
|
|
}()
|
|
|
|
// Test manual pagination
|
|
page1, err := testClient.Flows.List(ctx, &models.FlowFilter{
|
|
Tags: []string{"pagination-integration-test"},
|
|
}, 0, 5)
|
|
if err != nil {
|
|
t.Fatalf("Failed to list flows: %v", err)
|
|
}
|
|
if len(page1.Results) != 5 {
|
|
t.Errorf("Expected 5 flows in page 1, got %d", len(page1.Results))
|
|
}
|
|
if !page1.HasMore {
|
|
t.Error("Expected more pages")
|
|
}
|
|
|
|
// Test iterator
|
|
iter := testClient.Flows.ListAll(ctx, &models.FlowFilter{
|
|
Tags: []string{"pagination-integration-test"},
|
|
})
|
|
|
|
count := 0
|
|
for iter.Next(ctx) {
|
|
count++
|
|
}
|
|
if err := iter.Err(); err != nil {
|
|
t.Fatalf("Iterator error: %v", err)
|
|
}
|
|
if count != 15 {
|
|
t.Errorf("Expected to iterate over 15 flows, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestIntegration_Variables(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Create a variable
|
|
variable, err := testClient.Variables.Create(ctx, &models.VariableCreate{
|
|
Name: "test-var-" + uuid.New().String(),
|
|
Value: "test-value",
|
|
Tags: []string{"integration-test"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to create variable: %v", err)
|
|
}
|
|
defer testClient.Variables.Delete(ctx, variable.ID)
|
|
|
|
// Get the variable
|
|
retrievedVar, err := testClient.Variables.Get(ctx, variable.ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get variable: %v", err)
|
|
}
|
|
if retrievedVar.Value != "test-value" {
|
|
t.Errorf("Value mismatch: got %v, want test-value", retrievedVar.Value)
|
|
}
|
|
|
|
// Update the variable
|
|
newValue := "updated-value"
|
|
updatedVar, err := testClient.Variables.Update(ctx, variable.ID, &models.VariableUpdate{
|
|
Value: &newValue,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to update variable: %v", err)
|
|
}
|
|
if updatedVar.Value != newValue {
|
|
t.Errorf("Value mismatch: got %v, want %v", updatedVar.Value, newValue)
|
|
}
|
|
}
|
|
|
|
func TestIntegration_AdminEndpoints(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Test health check
|
|
if err := testClient.Admin.Health(ctx); err != nil {
|
|
t.Fatalf("Health check failed: %v", err)
|
|
}
|
|
|
|
// Test version
|
|
version, err := testClient.Admin.Version(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get version: %v", err)
|
|
}
|
|
if version == "" {
|
|
t.Error("Version is empty")
|
|
}
|
|
t.Logf("Server version: %s", version)
|
|
}
|
|
|
|
func TestIntegration_FlowRunWait(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Create a flow
|
|
flow, err := testClient.Flows.Create(ctx, &models.FlowCreate{
|
|
Name: "wait-test-" + uuid.New().String(),
|
|
Tags: []string{"integration-test"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to create flow: %v", err)
|
|
}
|
|
defer testClient.Flows.Delete(ctx, flow.ID)
|
|
|
|
// Create a flow run
|
|
flowRun, err := testClient.FlowRuns.Create(ctx, &models.FlowRunCreate{
|
|
FlowID: flow.ID,
|
|
Name: "wait-test-run",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to create flow run: %v", err)
|
|
}
|
|
defer testClient.FlowRuns.Delete(ctx, flowRun.ID)
|
|
|
|
// Simulate completion in background
|
|
go func() {
|
|
time.Sleep(2 * time.Second)
|
|
completedState := models.StateTypeCompleted
|
|
testClient.FlowRuns.SetState(ctx, flowRun.ID, &models.StateCreate{
|
|
Type: completedState,
|
|
Message: strPtr("Test completed"),
|
|
})
|
|
}()
|
|
|
|
// Wait for completion with timeout
|
|
waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
|
defer cancel()
|
|
|
|
finalRun, err := testClient.FlowRuns.Wait(waitCtx, flowRun.ID, time.Second)
|
|
if err != nil {
|
|
t.Fatalf("Failed to wait for flow run: %v", err)
|
|
}
|
|
|
|
if finalRun.StateType == nil || *finalRun.StateType != models.StateTypeCompleted {
|
|
t.Errorf("Expected COMPLETED state, got %v", finalRun.StateType)
|
|
}
|
|
}
|
|
|
|
func strPtr(s string) *string {
|
|
return &s
|
|
}
|