From c2c83961f8660d517698e550a28f053608a517c4 Mon Sep 17 00:00:00 2001 From: Marc Campbell Date: Thu, 24 Oct 2024 19:49:02 -0700 Subject: [PATCH] Integration test for release ls --- cli/cmd/release_ls.go | 20 ++++++++-- client/release.go | 7 ++-- pkg/integration/integration.go | 26 ++++++++++++ pkg/integration/release_test.go | 70 +++++++++++++++++++++++++++++++++ pkg/kotsclient/release.go | 8 ++-- pkg/kotsclient/types/types.go | 5 +++ pkg/platformclient/release.go | 4 +- 7 files changed, 128 insertions(+), 12 deletions(-) create mode 100644 pkg/integration/release_test.go diff --git a/cli/cmd/release_ls.go b/cli/cmd/release_ls.go index b18a2fce9..6bc60f297 100644 --- a/cli/cmd/release_ls.go +++ b/cli/cmd/release_ls.go @@ -1,7 +1,10 @@ package cmd import ( + "context" + "github.com/replicatedhq/replicated/cli/print" + "github.com/replicatedhq/replicated/pkg/integration" "github.com/spf13/cobra" ) @@ -10,16 +13,25 @@ func (r *runners) IniReleaseList(parent *cobra.Command) { Use: "ls", Short: "List all of an app's releases", Long: "List all of an app's releases", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + if integrationTest != "" { + ctx = context.WithValue(ctx, integration.IntegrationTestContextKey, integrationTest) + } + if logAPICalls != "" { + ctx = context.WithValue(ctx, integration.APICallLogContextKey, logAPICalls) + } + + return r.releaseList(ctx, cmd, args) + }, } parent.AddCommand(cmd) cmd.Flags().StringVar(&r.outputFormat, "output", "table", "The output format to use. One of: json|table (default: table)") - - cmd.RunE = r.releaseList } -func (r *runners) releaseList(cmd *cobra.Command, args []string) error { - releases, err := r.api.ListReleases(r.appID, r.appType) +func (r *runners) releaseList(ctx context.Context, cmd *cobra.Command, args []string) error { + releases, err := r.api.ListReleases(ctx, r.appID, r.appType) if err != nil { return err } diff --git a/client/release.go b/client/release.go index e8c69a52b..c7e1991ca 100644 --- a/client/release.go +++ b/client/release.go @@ -1,14 +1,15 @@ package client import ( + "context" "errors" "github.com/replicatedhq/replicated/pkg/types" ) -func (c *Client) ListReleases(appID string, appType string) ([]types.ReleaseInfo, error) { +func (c *Client) ListReleases(ctx context.Context, appID string, appType string) ([]types.ReleaseInfo, error) { if appType == "platform" { - platformReleases, err := c.PlatformClient.ListReleases(appID) + platformReleases, err := c.PlatformClient.ListReleases(ctx, appID) if err != nil { return nil, err } @@ -41,7 +42,7 @@ func (c *Client) ListReleases(appID string, appType string) ([]types.ReleaseInfo return releaseInfos, nil } else if appType == "kots" { - return c.KotsClient.ListReleases(appID) + return c.KotsClient.ListReleases(ctx, appID) } return nil, errors.New("unknown app type") diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index 2be4c3aba..c703589bf 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -3,6 +3,7 @@ package integration import ( "fmt" "path" + "time" kotsclienttypes "github.com/replicatedhq/replicated/pkg/kotsclient/types" "github.com/replicatedhq/replicated/pkg/types" @@ -33,6 +34,25 @@ var ( Name: "name", IsFoundation: true, } + + typesKOTSAppRelease = types.KotsAppRelease{ + AppID: "app-id", + Sequence: 1, + CreatedAt: time.Now(), + IsArchived: false, + Spec: "spec", + ReleaseNotes: "release-notes", + IsReleaseNotEditable: false, + Channels: []*types.Channel{ + { + ID: "channel-id", + Name: "channel-name", + }, + }, + Charts: []types.Chart{}, + CompatibilityResults: []types.CompatibilityResult{}, + IsHelmOnly: false, + } ) func Response(key string) interface{} { @@ -51,6 +71,12 @@ func Response(key string) interface{} { return &kotsclienttypes.CreateKOTSAppResponse{ App: &typeKOTSAppWithChannels, } + case "release-ls": + return kotsclienttypes.KotsListReleasesResponse{ + Releases: []*types.KotsAppRelease{ + &typesKOTSAppRelease, + }, + } default: panic(fmt.Sprintf("unknown integration test: %s", key)) } diff --git a/pkg/integration/release_test.go b/pkg/integration/release_test.go new file mode 100644 index 000000000..36d4a23ba --- /dev/null +++ b/pkg/integration/release_test.go @@ -0,0 +1,70 @@ +package integration + +import ( + "context" + "fmt" + "log" + "os" + "os/exec" + "strings" + "testing" + "time" +) + +func TestRelease(t *testing.T) { + tests := []struct { + name string + cli string + wantFormat format + wantLines int + wantAPIRequests []string + ignoreCLIOutput bool + }{ + { + name: "release-ls", + cli: "release ls", + wantFormat: FormatTable, + wantLines: 1, + wantAPIRequests: []string{ + "GET:/v3/app/id/releases", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + args := strings.Split(tt.cli, " ") + args = append(args, "--integration-test", tt.name) + + apiCallLog, err := os.CreateTemp("", "") + if err != nil { + log.Fatal(err) + } + + defer os.RemoveAll(apiCallLog.Name()) + + args = append(args, "--log-api-calls", apiCallLog.Name()) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cmd := exec.CommandContext(ctx, CLIPath(), args...) + + if ctx.Err() == context.DeadlineExceeded { + t.Fatalf("Command execution timed out") + } + + out, err := cmd.CombinedOutput() + if err != nil { + t.Errorf("error running cli: %v", err) + } + + fmt.Printf("out: %s\n", string(out)) + if !tt.ignoreCLIOutput { + AssertCLIOutput(t, string(out), tt.wantFormat, tt.wantLines) + } + + AssertAPIRequests(t, tt.wantAPIRequests, apiCallLog.Name()) + }) + } +} diff --git a/pkg/kotsclient/release.go b/pkg/kotsclient/release.go index 26eedeae3..851084608 100644 --- a/pkg/kotsclient/release.go +++ b/pkg/kotsclient/release.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/replicatedhq/replicated/pkg/graphql" + kotsclienttypes "github.com/replicatedhq/replicated/pkg/kotsclient/types" "github.com/replicatedhq/replicated/pkg/types" ) @@ -127,14 +128,15 @@ func (c *VendorV3Client) UpdateRelease(appID string, sequence int64, multiyaml s return nil } -func (c *VendorV3Client) ListReleases(appID string) ([]types.ReleaseInfo, error) { +func (c *VendorV3Client) ListReleases(ctx context.Context, appID string) ([]types.ReleaseInfo, error) { allReleases := []types.ReleaseInfo{} done := false page := 0 + for !done { - resp := types.KotsListReleasesResponse{} + resp := kotsclienttypes.KotsListReleasesResponse{} path := fmt.Sprintf("/v3/app/%s/releases?currentPage=%d&pageSize=20", appID, page) - err := c.DoJSON(context.TODO(), "GET", path, http.StatusOK, nil, &resp) + err := c.DoJSON(ctx, "GET", path, http.StatusOK, nil, &resp) if err != nil { done = true continue diff --git a/pkg/kotsclient/types/types.go b/pkg/kotsclient/types/types.go index 42580e5ac..be509be18 100644 --- a/pkg/kotsclient/types/types.go +++ b/pkg/kotsclient/types/types.go @@ -9,3 +9,8 @@ type KotsAppResponse struct { type CreateKOTSAppResponse struct { App *types.KotsAppWithChannels `json:"app"` } + +// KotsListReleasesResponse contains the JSON releases list +type KotsListReleasesResponse struct { + Releases []*types.KotsAppRelease `json:"releases"` +} diff --git a/pkg/platformclient/release.go b/pkg/platformclient/release.go index 35e25734f..0b909dd3f 100644 --- a/pkg/platformclient/release.go +++ b/pkg/platformclient/release.go @@ -12,10 +12,10 @@ import ( ) // ListReleases lists all releases for an app. -func (c *HTTPClient) ListReleases(appID string) ([]releases.AppReleaseInfo, error) { +func (c *HTTPClient) ListReleases(ctx context.Context, appID string) ([]releases.AppReleaseInfo, error) { path := fmt.Sprintf("/v1/app/%s/releases", appID) releases := make([]releases.AppReleaseInfo, 0) - if err := c.DoJSON(context.TODO(), "GET", path, http.StatusOK, nil, &releases); err != nil { + if err := c.DoJSON(ctx, "GET", path, http.StatusOK, nil, &releases); err != nil { return nil, fmt.Errorf("ListReleases: %w", err) } return releases, nil