diff --git a/CHANGELOG.md b/CHANGELOG.md index be03af0fe..baa965319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -279,6 +279,8 @@ - [v0.30.0](services/mariadb/CHANGELOG.md#v0300) - **Feature:** Introduce enums for various attributes - `modelexperiments`: + - [v.0.2.0](services/modelexperiments/CHANGELOG.md#v020) + - **New**: STACKIT Model Experiments module wait handler added. - [v0.1.0](services/modelexperiments/CHANGELOG.md#v010) - **New**: API for STACKIT modelexperiments - `modelserving`: diff --git a/examples/modelexperiments/go.mod b/examples/modelexperiments/go.mod new file mode 100644 index 000000000..7b5fb9fbf --- /dev/null +++ b/examples/modelexperiments/go.mod @@ -0,0 +1,14 @@ +module github.com/stackitcloud/stackit-sdk-go/examples/modelexperiments + +go 1.25 + +// This is not needed in production. This is only here to point the golangci linter to the local version instead of the last release on GitHub. +replace github.com/stackitcloud/stackit-sdk-go/services/modelexperiments => ../../services/modelexperiments + +require github.com/stackitcloud/stackit-sdk-go/services/modelexperiments v0.2.0 + +require ( + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/stackitcloud/stackit-sdk-go/core v0.26.0 // indirect +) diff --git a/examples/modelexperiments/go.sum b/examples/modelexperiments/go.sum new file mode 100644 index 000000000..3712a0c87 --- /dev/null +++ b/examples/modelexperiments/go.sum @@ -0,0 +1,8 @@ +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/stackitcloud/stackit-sdk-go/core v0.26.0 h1:jQEb9gkehfp6VCP6TcYk7BI10cz4l0KM2L6hqYBH2QA= +github.com/stackitcloud/stackit-sdk-go/core v0.26.0/go.mod h1:WU1hhxnjXw2EV7CYa1nlEvNpMiRY6CvmIOaHuL3pOaA= diff --git a/examples/modelexperiments/modelexperiments.go b/examples/modelexperiments/modelexperiments.go new file mode 100644 index 000000000..5455725c7 --- /dev/null +++ b/examples/modelexperiments/modelexperiments.go @@ -0,0 +1,149 @@ +package main + +import ( + "context" + "log" + + modelexperiments "github.com/stackitcloud/stackit-sdk-go/services/modelexperiments/v1api" + "github.com/stackitcloud/stackit-sdk-go/services/modelexperiments/v1api/wait" +) + +func main() { + projectId := "PROJECT_ID" // the uuid of your STACKIT project + region := "eu01" + instanceName := "instance" + newInstanceName := "newInstance" + newTokenName := "newTokenName" + description := "description" + ctx := context.Background() + + modelexperimentsClient, err := modelexperiments.NewAPIClient() + if err != nil { + log.Fatalf("[Model Experiments] Creating API client: %v\n", err) + } + + // Create a Model Experiments Instance + log.Printf("[Model Experiments] Creating Model Experiments Instance.\n") + createInstancePayload := modelexperiments.CreateInstancePayload{ + Name: instanceName, + Description: &description, + } + createInstanceResp, err := modelexperimentsClient.DefaultAPI.CreateInstance(ctx, projectId, region).CreateInstancePayload(createInstancePayload).Execute() + if err != nil { + log.Fatalf("[Model Experiments] Error when calling `CreateInstance`: %v\n", err) + } + log.Printf("[Model Experiments] Triggered creation of Model Experiments Instance with ID: \"%s\".\n", createInstanceResp.Instance.Id) + + // Wait for the Model Experiments Instance to be ready + log.Printf("[Model Experiments] Waiting for Model Experiments Instance to be created.\n") + _, err = wait.CreateInstanceWaitHandler(ctx, modelexperimentsClient.DefaultAPI, region, projectId, createInstanceResp.Instance.Id).WaitWithContext(ctx) + if err != nil { + log.Fatalf("[Model Experiments] Error when waiting for creation: %v\n", err) + } + log.Printf("[Model Experiments] Model Experiments Instance \"%s\" has been successfully created.\n", createInstanceResp.Instance.Id) + + // Get the created Model Experiments Instance + log.Printf("[Model Experiments] Retrieving Model Experiments Instance.\n") + getInstanceResp, err := modelexperimentsClient.DefaultAPI.GetInstance(ctx, projectId, region, createInstanceResp.Instance.Id).Execute() + if err != nil { + log.Fatalf("[Model Experiments] Error when calling `GetInstance`: %v\n", err) + } + log.Printf("[Model Experiments] Retrieved Model Experiments Instance with ID: \"%s\" and display name: \"%s\"\n", getInstanceResp.Instance.Id, getInstanceResp.Instance.Name) + + // List Model Experiments Instances + log.Printf("[Model Experiments] Listing Model Experiments Instances.\n") + listInstanceResp, err := modelexperimentsClient.DefaultAPI.ListInstances(ctx, projectId, region).Execute() + if err != nil { + log.Fatalf("[Model Experiments] Error when calling `ListInstances`: %v\n", err) + } + log.Printf("[Model Experiments] Retrieved %d instances\n", len(listInstanceResp.Instances)) + + // Update a Model Experiments Instance + log.Printf("[Model Experiments] Updating Model Experiments Instance.\n") + partialUpdateInstancePayload := modelexperiments.PartialUpdateInstancePayload{ + Name: &newInstanceName, + } + partialUpdateInstanceResp, err := modelexperimentsClient.DefaultAPI.PartialUpdateInstance(ctx, projectId, region, getInstanceResp.Instance.Id).PartialUpdateInstancePayload(partialUpdateInstancePayload).Execute() + if err != nil { + log.Fatalf("[Model Experiments] Error when calling `PartialUpdateInstance`: %v\n", err) + } + log.Printf("[Model Experiments] Updated Model Experiments Instance with ID: \"%s\" and display name: \"%s\"\n", partialUpdateInstanceResp.Instance.Id, partialUpdateInstanceResp.Instance.Name) + + // Create a Model Experiments Instance Token + log.Printf("[Model Experiments] Creating Model Experiments Instance Token.\n") + createTokenPayload := modelexperiments.CreateInstanceTokenPayload{ + Name: "token-name", + } + createTokenResp, err := modelexperimentsClient.DefaultAPI.CreateInstanceToken(ctx, projectId, region, createInstanceResp.Instance.Id).CreateInstanceTokenPayload(createTokenPayload).Execute() + if err != nil { + log.Fatalf("[Model Experiments] Error when calling `CreateToken`: %v\n", err) + } + log.Printf("[Model Experiments] Triggered creation of Model Experiments Instance Token: %+v\n", createTokenResp.Token) + + // Wait for the Model Experiments Instance Token to be ready + log.Printf("[Model Experiments] Waiting for Model Experiments Instance Token to be created.\n") + _, err = wait.CreateInstanceTokenWaitHandler(ctx, modelexperimentsClient.DefaultAPI, region, projectId, getInstanceResp.Instance.Id, createTokenResp.Token.Id).WaitWithContext(ctx) + if err != nil { + log.Fatalf("[Model Experiments] Error when waiting for creation: %v\n", err) + } + log.Printf("[Model Experiments] Model Experiments Instance Token \"%s\" has been successfully created.\n", createTokenResp.Token.Id) + + // Get the created Model Experiments Instance Token + log.Printf("[Model Experiments] Retrieving Model Experiments Instance Token.\n") + getTokenResp, err := modelexperimentsClient.DefaultAPI.GetInstanceToken(ctx, projectId, region, createTokenResp.Token.Id, getInstanceResp.Instance.Id).Execute() + if err != nil { + log.Fatalf("[Model Experiments] Error when calling `GetInstanceToken`: %v\n", err) + } + log.Printf("[Model Experiments] Retrieved Model Experiments Instance Token with ID: \"%s\" and display name: \"%s\"\n", getTokenResp.Token.Id, getTokenResp.Token.Name) + + // List Model Experiments Instance Tokens + log.Printf("[Model Experiments] Listing Model Experiments Instance Tokens.\n") + listTokenResp, err := modelexperimentsClient.DefaultAPI.ListInstanceTokens(ctx, projectId, region, getInstanceResp.Instance.Id).Execute() + if err != nil { + log.Fatalf("[Model Experiments] Error when calling `ListInstanceTokens`: %v\n", err) + } + log.Printf("[Model Experiments] Retrieved %d Instance tokens\n", len(listTokenResp.Tokens)) + + // Update a Model Experiments Instance Token + log.Printf("[Model Experiments] Updating Model Experiments Instance Token.\n") + partialUpdateInstanceTokenPayload := modelexperiments.PartialUpdateInstanceTokenPayload{ + Name: &newTokenName, + } + partialUpdateTokenResp, err := modelexperimentsClient.DefaultAPI.PartialUpdateInstanceToken(ctx, projectId, region, getTokenResp.Token.Id, getInstanceResp.Instance.Id).PartialUpdateInstanceTokenPayload(partialUpdateInstanceTokenPayload).Execute() + if err != nil { + log.Fatalf("[Model Experiments] Error when calling `PartialUpdateInstanceToken`: %v\n", err) + } + log.Printf("[Model Experiments] Updated Model Experiments Instance Token with ID: \"%s\" and display name: \"%s\"\n", partialUpdateTokenResp.Token.Id, partialUpdateTokenResp.Token.Name) + + // Delete a Model Experiments Instance Token + log.Printf("[Model Experiments] Deleting Model Experiments Instance Token.\n") + deleteTokenResp, err := modelexperimentsClient.DefaultAPI.DeleteInstanceToken(ctx, projectId, region, getTokenResp.Token.Id, getInstanceResp.Instance.Id).Execute() + if err != nil { + log.Fatalf("[Model Experiments] Error when calling `DeleteInstanceToken`: %v\n", err) + } + log.Printf("[Model Experiments] Triggered deletion of Model Experiments Instance Token with ID: \"%s\" and display name: \"%s\"\n", deleteTokenResp.Token.Id, deleteTokenResp.Token.Name) + + // Wait for the Model Experiments Instance Token to be deleted + log.Printf("[Model Experiments] Waiting for Model Experiments Instance Token to be deleted.\n") + _, err = wait.DeleteInstanceTokenWaitHandler(ctx, modelexperimentsClient.DefaultAPI, region, projectId, getInstanceResp.Instance.Id, deleteTokenResp.Token.Id).WaitWithContext(ctx) + if err != nil { + log.Fatalf("[Model Experiments] Error when waiting for deletion: %v\n", err) + } + log.Printf("[Model Experiments] Model Experiments Instance Token \"%s\" has been successfully deleted.\n", deleteTokenResp.Token.Id) + + // Delete a Model Experiments Instance + log.Printf("[Model Experiments] Deleting Model Experiments Instance.\n") + deleteInstanceResp, err := modelexperimentsClient.DefaultAPI.DeleteInstance(ctx, projectId, region, getInstanceResp.Instance.Id).Execute() + if err != nil { + log.Fatalf("[Model Experiments] Error when calling `DeleteInstance`: %v\n", err) + } + log.Printf("[Model Experiments] Triggered deletion of Model Experiments Instance with ID: \"%s\" and display name: \"%s\"\n", deleteInstanceResp.Instance.Id, deleteInstanceResp.Instance.Name) + + // Wait for the Model Experiments Instance to be deleted + log.Printf("[Model Experiments] Waiting for Model Experiments Instance to be deleted.\n") + _, err = wait.DeleteInstanceWaitHandler(ctx, modelexperimentsClient.DefaultAPI, region, projectId, getInstanceResp.Instance.Id).WaitWithContext(ctx) + if err != nil { + log.Fatalf("[Model Experiments] Error when waiting for deletion: %v\n", err) + } + log.Printf("[Model Experiments] Model Experiments Instance \"%s\" has been successfully deleted.\n", deleteInstanceResp.Instance.Id) +} diff --git a/go.work b/go.work index 9d64b69f9..7c0013e6d 100644 --- a/go.work +++ b/go.work @@ -20,6 +20,7 @@ use ( ./examples/logs ./examples/mariadb ./examples/middleware + ./examples/modelexperiments ./examples/mongodbflex ./examples/objectstorage ./examples/observability diff --git a/services/modelexperiments/CHANGELOG.md b/services/modelexperiments/CHANGELOG.md index 4794a642e..55a0c150e 100644 --- a/services/modelexperiments/CHANGELOG.md +++ b/services/modelexperiments/CHANGELOG.md @@ -1,2 +1,5 @@ +## v0.2.0 +- **New**: STACKIT Model Experiments module wait handler added. + ## v0.1.0 - **New**: API for STACKIT modelexperiments \ No newline at end of file diff --git a/services/modelexperiments/NOTICE.txt b/services/modelexperiments/NOTICE.txt index 80f267471..ae5b9e7f3 100644 --- a/services/modelexperiments/NOTICE.txt +++ b/services/modelexperiments/NOTICE.txt @@ -1,2 +1,2 @@ -STACKIT modelexperiments SDK for Go +STACKIT Model Experiments SDK for Go Copyright 2026 Schwarz IT KG \ No newline at end of file diff --git a/services/modelexperiments/VERSION b/services/modelexperiments/VERSION index 9ff151c5b..81fd7ba08 100644 --- a/services/modelexperiments/VERSION +++ b/services/modelexperiments/VERSION @@ -1 +1 @@ -v0.1.0 \ No newline at end of file +v0.2.0 \ No newline at end of file diff --git a/services/modelexperiments/go.mod b/services/modelexperiments/go.mod index 6ea1a4c6f..b62e53f0e 100644 --- a/services/modelexperiments/go.mod +++ b/services/modelexperiments/go.mod @@ -2,7 +2,10 @@ module github.com/stackitcloud/stackit-sdk-go/services/modelexperiments go 1.25 -require github.com/stackitcloud/stackit-sdk-go/core v0.26.0 +require ( + github.com/google/go-cmp v0.7.0 + github.com/stackitcloud/stackit-sdk-go/core v0.26.0 +) require ( github.com/golang-jwt/jwt/v5 v5.3.1 // indirect diff --git a/services/modelexperiments/v1api/wait/wait.go b/services/modelexperiments/v1api/wait/wait.go new file mode 100644 index 000000000..04fa556f8 --- /dev/null +++ b/services/modelexperiments/v1api/wait/wait.go @@ -0,0 +1,92 @@ +package wait + +import ( + "context" + "errors" + "net/http" + "time" + + "github.com/stackitcloud/stackit-sdk-go/core/wait" + modelexperiments "github.com/stackitcloud/stackit-sdk-go/services/modelexperiments/v1api" +) + +const ( + INSTANCESTATE_ACTIVE = "active" + INSTANCESTATE_IMPAIRED = "impaired" + + TOKENSTATE_ACTIVE = "active" +) + +// CreateInstanceWaitHandler will wait for creation of Model Experiments instance +func CreateInstanceWaitHandler(ctx context.Context, a modelexperiments.DefaultAPI, region, projectId, instanceId string) *wait.AsyncActionHandler[modelexperiments.GetInstanceResponse] { + waitConfig := wait.WaiterHelper[modelexperiments.GetInstanceResponse, modelexperiments.InstanceState]{ + FetchInstance: a.GetInstance(ctx, projectId, region, instanceId).Execute, + GetState: func(response *modelexperiments.GetInstanceResponse) (modelexperiments.InstanceState, error) { + if response == nil { + return "", errors.New("empty response") + } + return response.Instance.State, nil + }, + ActiveState: []modelexperiments.InstanceState{modelexperiments.INSTANCESTATE_ACTIVE}, + ErrorState: []modelexperiments.InstanceState{modelexperiments.INSTANCESTATE_IMPAIRED}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(45 * time.Minute) + return handler +} + +// DeleteInstanceWaitHandler will wait for deletion of Model Experiments instance +func DeleteInstanceWaitHandler(ctx context.Context, a modelexperiments.DefaultAPI, region, projectId, instanceId string) *wait.AsyncActionHandler[modelexperiments.GetInstanceResponse] { + waitConfig := wait.WaiterHelper[modelexperiments.GetInstanceResponse, modelexperiments.InstanceState]{ + FetchInstance: a.GetInstance(ctx, projectId, region, instanceId).Execute, + GetState: func(response *modelexperiments.GetInstanceResponse) (modelexperiments.InstanceState, error) { + if response == nil { + return "", errors.New("empty response") + } + return response.Instance.State, nil + }, + DeleteHttpErrorStatusCodes: []int{http.StatusNotFound}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(45 * time.Minute) + return handler +} + +// CreateInstanceTokenWaitHandler will wait for creation of Model Experiments instance token +func CreateInstanceTokenWaitHandler(ctx context.Context, a modelexperiments.DefaultAPI, region, projectId, instanceId, tokenId string) *wait.AsyncActionHandler[modelexperiments.GetInstanceTokenResponse] { + waitConfig := wait.WaiterHelper[modelexperiments.GetInstanceTokenResponse, modelexperiments.TokenState]{ + FetchInstance: a.GetInstanceToken(ctx, projectId, region, tokenId, instanceId).Execute, + GetState: func(response *modelexperiments.GetInstanceTokenResponse) (modelexperiments.TokenState, error) { + if response == nil { + return "", errors.New("empty response") + } + return response.Token.State, nil + }, + ActiveState: []modelexperiments.TokenState{modelexperiments.TOKENSTATE_ACTIVE}, + ErrorState: []modelexperiments.TokenState{}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(45 * time.Minute) + return handler +} + +// DeleteInstanceTokenWaitHandler will wait for deletion of Model Experiments instance token +func DeleteInstanceTokenWaitHandler(ctx context.Context, a modelexperiments.DefaultAPI, region, projectId, instanceId, tokenId string) *wait.AsyncActionHandler[modelexperiments.GetInstanceTokenResponse] { + waitConfig := wait.WaiterHelper[modelexperiments.GetInstanceTokenResponse, modelexperiments.TokenState]{ + FetchInstance: a.GetInstanceToken(ctx, projectId, region, tokenId, instanceId).Execute, + GetState: func(response *modelexperiments.GetInstanceTokenResponse) (modelexperiments.TokenState, error) { + if response == nil { + return "", errors.New("empty response") + } + return response.Token.State, nil + }, + DeleteHttpErrorStatusCodes: []int{http.StatusNotFound}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(45 * time.Minute) + return handler +} diff --git a/services/modelexperiments/v1api/wait/wait_test.go b/services/modelexperiments/v1api/wait/wait_test.go new file mode 100644 index 000000000..7acfcdac4 --- /dev/null +++ b/services/modelexperiments/v1api/wait/wait_test.go @@ -0,0 +1,327 @@ +package wait + +import ( + "context" + "testing" + "testing/synctest" + "time" + + "github.com/google/go-cmp/cmp" + + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + "github.com/stackitcloud/stackit-sdk-go/core/utils" + modelexperiments "github.com/stackitcloud/stackit-sdk-go/services/modelexperiments/v1api" +) + +type mockSettings struct { + getFails bool + statusCode int + resourceState string +} + +func newAPIMock(settings *mockSettings) modelexperiments.DefaultAPI { + return &modelexperiments.DefaultAPIServiceMock{ + GetInstanceExecuteMock: utils.Ptr(func(_ modelexperiments.ApiGetInstanceRequest) (*modelexperiments.GetInstanceResponse, error) { + if settings.getFails { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: settings.statusCode, + } + } + + return &modelexperiments.GetInstanceResponse{ + Instance: modelexperiments.Instance{ + State: modelexperiments.InstanceState(settings.resourceState), + }, + }, nil + }), + GetInstanceTokenExecuteMock: utils.Ptr(func(_ modelexperiments.ApiGetInstanceTokenRequest) (*modelexperiments.GetInstanceTokenResponse, error) { + if settings.getFails { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: settings.statusCode, + } + } + + return &modelexperiments.GetInstanceTokenResponse{ + Token: modelexperiments.TokenMetadata{ + State: modelexperiments.TokenState(settings.resourceState), + }, + }, nil + }), + } +} + +func TestCreateModelExperimentsInstanceWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + statusCode int + resourceState string + wantErr bool + wantResp bool + }{ + { + desc: "create_succeeded", + getFails: false, + statusCode: 202, + resourceState: string(modelexperiments.INSTANCESTATE_ACTIVE), + wantErr: false, + wantResp: true, + }, + { + desc: "get_fails", + getFails: true, + statusCode: 500, + resourceState: "", + wantErr: true, + wantResp: false, + }, + { + desc: "not_found", + getFails: true, + statusCode: 404, + resourceState: "", + wantErr: true, + wantResp: false, + }, + { + desc: "impaired", + getFails: false, + statusCode: 200, + resourceState: string(modelexperiments.INSTANCESTATE_IMPAIRED), + wantErr: true, + wantResp: true, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(&mockSettings{ + getFails: tt.getFails, + statusCode: tt.statusCode, + resourceState: tt.resourceState, + }) + + var wantRes *modelexperiments.GetInstanceResponse + if tt.wantResp { + wantRes = &modelexperiments.GetInstanceResponse{ + Instance: modelexperiments.Instance{ + State: modelexperiments.InstanceState(tt.resourceState), + }, + } + } + + handler := CreateInstanceWaitHandler(context.Background(), apiClient, "region", "pid", "instanceId") + + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }) + }) + } +} + +func TestDeleteModelExperimentsInstanceWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + statusCode int + resourceState string + wantErr bool + wantResp bool + }{ + { + desc: "delete_succeeded", + getFails: true, + statusCode: 404, + resourceState: "", + wantErr: false, + wantResp: false, + }, + { + desc: "delete_in_progress", + getFails: false, + statusCode: 200, + resourceState: string(modelexperiments.INSTANCESTATE_DELETING), + wantErr: true, // Should timeout since delete is not complete + wantResp: false, + }, + { + desc: "get_fails_with_other_error", + getFails: true, + statusCode: 500, + resourceState: "", + wantErr: true, + wantResp: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(&mockSettings{ + getFails: tt.getFails, + statusCode: tt.statusCode, + resourceState: tt.resourceState, + }) + + var wantRes *modelexperiments.GetInstanceResponse + if tt.wantResp { + wantRes = &modelexperiments.GetInstanceResponse{ + Instance: modelexperiments.Instance{ + State: modelexperiments.InstanceState(tt.resourceState), + }, + } + } + + handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "region", "pid", "instanceId") + + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }) + }) + } +} + +func TestCreateModelExperimentsInstanceTokenWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + statusCode int + resourceState string + wantErr bool + wantResp bool + }{ + { + desc: "create_succeeded", + getFails: false, + statusCode: 202, + resourceState: string(modelexperiments.TOKENSTATE_ACTIVE), + wantErr: false, + wantResp: true, + }, + { + desc: "get_fails", + getFails: true, + statusCode: 500, + resourceState: "", + wantErr: true, + wantResp: false, + }, + { + desc: "not_found", + getFails: true, + statusCode: 404, + resourceState: "", + wantErr: true, + wantResp: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(&mockSettings{ + getFails: tt.getFails, + statusCode: tt.statusCode, + resourceState: tt.resourceState, + }) + + var wantRes *modelexperiments.GetInstanceTokenResponse + if tt.wantResp { + wantRes = &modelexperiments.GetInstanceTokenResponse{ + Token: modelexperiments.TokenMetadata{ + State: modelexperiments.TokenState(tt.resourceState), + }, + } + } + + handler := CreateInstanceTokenWaitHandler(context.Background(), apiClient, "region", "pid", "instanceId", "tid") + + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }) + }) + } +} + +func TestDeleteModelExperimentsInstanceTokenWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + statusCode int + resourceState string + wantErr bool + wantResp bool + }{ + { + desc: "delete_succeeded", + getFails: true, + statusCode: 404, + resourceState: "", + wantErr: false, + wantResp: false, + }, + { + desc: "delete_in_progress", + getFails: false, + statusCode: 200, + resourceState: string(modelexperiments.TOKENSTATE_DELETING), + wantErr: true, // Should timeout since delete is not complete + wantResp: false, + }, + { + desc: "get_fails_with_other_error", + getFails: true, + statusCode: 500, + resourceState: "", + wantErr: true, + wantResp: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(&mockSettings{ + getFails: tt.getFails, + statusCode: tt.statusCode, + resourceState: tt.resourceState, + }) + + var wantRes *modelexperiments.GetInstanceTokenResponse + if tt.wantResp { + wantRes = &modelexperiments.GetInstanceTokenResponse{ + Token: modelexperiments.TokenMetadata{ + State: modelexperiments.TokenState(tt.resourceState), + }, + } + } + + handler := DeleteInstanceTokenWaitHandler(context.Background(), apiClient, "region", "pid", "instanceId", "tid") + + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }) + }) + } +}