-
Notifications
You must be signed in to change notification settings - Fork 768
[Test] Add Task service api test structure and for DeployTask #6823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0d7fe4b
896f206
a9f3f25
4c9a337
5c20324
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| package api | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "log" | ||
| "net/http" | ||
| "os" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "golang.org/x/net/http2" | ||
| "golang.org/x/net/http2/h2c" | ||
| "gorm.io/gorm" | ||
|
|
||
| "github.com/flyteorg/flyte/v2/flytestdlib/database" | ||
| "github.com/flyteorg/flyte/v2/flytestdlib/logger" | ||
| "github.com/flyteorg/flyte/v2/gen/go/flyteidl2/task/taskconnect" | ||
| "github.com/flyteorg/flyte/v2/runs/migrations" | ||
| "github.com/flyteorg/flyte/v2/runs/repository" | ||
| "github.com/flyteorg/flyte/v2/runs/service" | ||
| ) | ||
|
|
||
| const ( | ||
| testPort = 8091 // Different port to avoid conflicts with main service | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just pure out of curiosity. Will people have their port 8091 being used❓ |
||
| testDBFile = "/tmp/flyte-runs-test.db" // Temporary database file for tests | ||
| ) | ||
|
|
||
| var ( | ||
| endpoint string | ||
| testServer *http.Server | ||
| testDB *gorm.DB // Expose DB for cleanup | ||
| ) | ||
|
|
||
|
|
||
| // TestMain sets up the test environment with SQLite database and runs service | ||
| func TestMain(m *testing.M) { | ||
| ctx := context.Background() | ||
| var exitCode int | ||
| defer func() { | ||
| os.Exit(exitCode) | ||
| }() | ||
|
|
||
| // Setup: Create SQLite database | ||
| // NOTE: Using a temp file instead of :memory: to avoid GORM AutoMigrate issues | ||
| // with index creation on fresh in-memory databases | ||
| dbConfig := &database.DbConfig{ | ||
| SQLite: database.SQLiteConfig{ | ||
| File: testDBFile, | ||
| }, | ||
| } | ||
| logCfg := logger.GetConfig() | ||
| var err error | ||
| testDB, err = database.GetDB(ctx, dbConfig, logCfg) | ||
| if err != nil { | ||
| log.Fatalf("Failed to initialize database: %v", err) | ||
| } | ||
| log.Println("Database initialized") | ||
|
|
||
| // Run migrations | ||
| if err := migrations.RunMigrations(testDB); err != nil { | ||
| log.Fatalf("Failed to run migrations: %v", err) | ||
| } | ||
| log.Println("Database migrations completed") | ||
|
|
||
| // Create repository and services | ||
| repo := repository.NewRepository(testDB) | ||
| taskSvc := service.NewTaskService(repo) | ||
|
|
||
| // Setup HTTP server | ||
| mux := http.NewServeMux() | ||
| taskPath, taskHandler := taskconnect.NewTaskServiceHandler(taskSvc) | ||
| mux.Handle(taskPath, taskHandler) | ||
|
|
||
| // Add health check | ||
| mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { | ||
| w.WriteHeader(http.StatusOK) | ||
| _, _ = w.Write([]byte("OK")) | ||
| }) | ||
|
|
||
| endpoint = fmt.Sprintf("http://localhost:%d", testPort) | ||
| testServer = &http.Server{ | ||
| Addr: fmt.Sprintf(":%d", testPort), | ||
| Handler: h2c.NewHandler(mux, &http2.Server{}), | ||
| } | ||
|
|
||
| // Start server in background | ||
| go func() { | ||
| log.Printf("Test server starting on %s", endpoint) | ||
| if err := testServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { | ||
| log.Printf("Server error: %v", err) | ||
| } | ||
| }() | ||
|
|
||
| // Wait for server to be ready | ||
| if !waitForServer(endpoint, 10*time.Second) { | ||
| log.Fatal("Test server failed to start") | ||
| } | ||
|
Comment on lines
+88
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the server fails to bind to port 8091, besides logging, the code still keeps on running for 10 seconds. Do you think it's a good idea to stop early whenever the server fails to bind in the goroutine? |
||
| log.Println("Test server is ready") | ||
|
|
||
| // Run tests | ||
| exitCode = m.Run() | ||
|
|
||
| // Teardown: Stop server | ||
| shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
| defer cancel() | ||
| if err := testServer.Shutdown(shutdownCtx); err != nil { | ||
| log.Printf("Test server shutdown error: %v", err) | ||
| } | ||
| log.Println("Test server stopped") | ||
|
|
||
| // Cleanup: Remove test database file | ||
| if err := os.Remove(testDBFile); err != nil && !os.IsNotExist(err) { | ||
| log.Printf("Warning: Failed to remove test database: %v", err) | ||
| } | ||
|
Comment on lines
+104
to
+115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here cleanup only runs if we reach the end of TestMain. If there’s a panic or early exit during setup, the server and DB file might be left around. Would it make sense to move the cleanup into the deferred block so it always runs? |
||
| } | ||
|
|
||
| // waitForServer waits for the server to be ready | ||
| func waitForServer(url string, timeout time.Duration) bool { | ||
| client := &http.Client{Timeout: 1 * time.Second} | ||
| deadline := time.Now().Add(timeout) | ||
|
|
||
| for time.Now().Before(deadline) { | ||
| resp, err := client.Get(url + "/healthz") | ||
| if err == nil && resp.StatusCode == http.StatusOK { | ||
| resp.Body.Close() | ||
| return true | ||
| } | ||
| if resp != nil { | ||
| resp.Body.Close() | ||
| } | ||
| time.Sleep(100 * time.Millisecond) | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| // cleanupTestDB clears all tables in the test database | ||
| // This ensures each test starts with a clean state | ||
| func cleanupTestDB(t *testing.T) { | ||
| t.Helper() | ||
|
|
||
| if testDB == nil { | ||
| t.Log("Warning: testDB is nil, skipping cleanup") | ||
| return | ||
| } | ||
|
|
||
| // Delete all records from all tables | ||
| // Order matters due to foreign key constraints (if any) | ||
| tables := []string{"tasks", "task_specs", "actions"} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I acknowledge we only got these three tables right now. But what do you feel about sending query for dynamic table check? |
||
|
|
||
| for _, table := range tables { | ||
| if err := testDB.Exec(fmt.Sprintf("DELETE FROM %s", table)).Error; err != nil { | ||
| t.Logf("Warning: Failed to cleanup table %s: %v", table, err) | ||
| } | ||
| } | ||
|
Comment on lines
+151
to
+155
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it'll be better to change it to |
||
|
|
||
| t.Log("Test database cleaned up") | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| package api | ||
|
|
||
| import ( | ||
| "context" | ||
| "testing" | ||
|
|
||
| "connectrpc.com/connect" | ||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
|
|
||
| "github.com/flyteorg/flyte/v2/gen/go/flyteidl2/core" | ||
| "github.com/flyteorg/flyte/v2/gen/go/flyteidl2/task" | ||
| "github.com/flyteorg/flyte/v2/gen/go/flyteidl2/task/taskconnect" | ||
| ) | ||
|
|
||
| const ( | ||
| testOrg = "test-org" | ||
| testProject = "test-project" | ||
| testDomain = "test-domain" | ||
| ) | ||
|
|
||
| func TestDeployTask(t *testing.T) { | ||
| t.Cleanup(func() { | ||
| cleanupTestDB(t) | ||
| }) | ||
|
|
||
| ctx := context.Background() | ||
| httpClient := newClient() | ||
| opts := []connect.ClientOption{} | ||
|
|
||
| taskClient := taskconnect.NewTaskServiceClient(httpClient, endpoint, opts...) | ||
|
|
||
| // Deploy a task | ||
| taskID := &task.TaskIdentifier{ | ||
| Org: testOrg, | ||
| Project: testProject, | ||
| Domain: testDomain, | ||
| Name: "test-task", | ||
| Version: uniqueString(), | ||
| } | ||
|
|
||
| deployResp, err := taskClient.DeployTask(ctx, connect.NewRequest(&task.DeployTaskRequest{ | ||
| TaskId: taskID, | ||
| Spec: &task.TaskSpec{ | ||
| TaskTemplate: &core.TaskTemplate{ | ||
| Type: "container", | ||
| Target: &core.TaskTemplate_Container{ | ||
| Container: &core.Container{ | ||
| Image: "alpine:latest", | ||
| Args: []string{"echo", "hello"}, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| })) | ||
|
|
||
| require.NoError(t, err) | ||
| require.NotNil(t, deployResp) | ||
| t.Logf("Task deployed successfully: %v", taskID) | ||
|
|
||
| // Get task details | ||
| getTaskDetailsResp, err := taskClient.GetTaskDetails(ctx, connect.NewRequest(&task.GetTaskDetailsRequest{ | ||
| TaskId: taskID, | ||
| })) | ||
|
|
||
| require.NoError(t, err) | ||
| require.NotNil(t, getTaskDetailsResp) | ||
| details := getTaskDetailsResp.Msg.GetDetails() | ||
| assert.Equal(t, taskID.GetOrg(), details.GetTaskId().GetOrg()) | ||
| assert.Equal(t, taskID.GetProject(), details.GetTaskId().GetProject()) | ||
| assert.Equal(t, taskID.GetDomain(), details.GetTaskId().GetDomain()) | ||
| assert.Equal(t, taskID.GetName(), details.GetTaskId().GetName()) | ||
| assert.Equal(t, taskID.GetVersion(), details.GetTaskId().GetVersion()) | ||
| t.Logf("Task details retrieved successfully: %v", details) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package api | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "net/http" | ||
| "os" | ||
| "time" | ||
| ) | ||
|
|
||
| func getEnvOrDefault(key, defaultValue string) string { | ||
| if value, exists := os.LookupEnv(key); exists { | ||
| return value | ||
| } | ||
| return defaultValue | ||
| } | ||
|
Comment on lines
+10
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm I think this function was never used right 🤔 |
||
|
|
||
| func uniqueString() string { | ||
| return fmt.Sprintf("%d", time.Now().UnixNano()) | ||
| } | ||
|
|
||
| func newClient() *http.Client { | ||
| return &http.Client{ | ||
| Timeout: 30 * time.Second, | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a small typo here -> "Please"