| // Copyright IBM Corp. 2014, 2026 | |
| // SPDX-License-Identifier: BUSL-1.1 | |
| package pluginshared | |
| import ( | |
| "bytes" | |
| "context" | |
| "errors" | |
| "io" | |
| "net/http" | |
| "net/http/httptest" | |
| "net/url" | |
| "testing" | |
| "time" | |
| ) | |
| func newHTTPTestServerUnsupported(t *testing.T) (*httptest.Server, error) { | |
| t.Helper() | |
| return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { | |
| w.WriteHeader(http.StatusNotFound) | |
| w.Write([]byte("404 Not Found")) | |
| })), nil | |
| } | |
| func TestCloudPluginClient_DownloadFile(t *testing.T) { | |
| server, err := newCloudPluginManifestHTTPTestServer(t) | |
| if err != nil { | |
| t.Fatalf("could not create test server: %s", err) | |
| } | |
| defer server.Close() | |
| serverURL, _ := url.Parse(server.URL) | |
| serviceURL := serverURL.JoinPath("/api/cloudplugin/v1") | |
| client, err := NewCloudPluginClient(context.Background(), serviceURL) | |
| if err != nil { | |
| t.Fatalf("could not create test client: %s", err) | |
| } | |
| t.Run("200 response", func(t *testing.T) { | |
| buffer := bytes.Buffer{} | |
| err := client.DownloadFile("/archives/terraform-cloudplugin_0.1.0_SHA256SUMS", &buffer) | |
| if err != nil { | |
| t.Fatal("expected no error") | |
| } | |
| if buffer.Len() == 0 { | |
| t.Error("expected data, but got none") | |
| } | |
| }) | |
| t.Run("404 response", func(t *testing.T) { | |
| err := client.DownloadFile("/archives/nope.zip", io.Discard) | |
| if !errors.Is(err, ErrPluginNotFound) { | |
| t.Fatalf("expected error %q, got %q", ErrPluginNotFound, err) | |
| } | |
| }) | |
| } | |
| func TestCloudPluginClient_FetchManifest(t *testing.T) { | |
| server, err := newCloudPluginManifestHTTPTestServer(t) | |
| if err != nil { | |
| t.Fatalf("could not create test server: %s", err) | |
| } | |
| defer server.Close() | |
| serverURL, _ := url.Parse(server.URL) | |
| serviceURL := serverURL.JoinPath("/api/cloudplugin/v1") | |
| client, err := NewCloudPluginClient(context.Background(), serviceURL) | |
| if err != nil { | |
| t.Fatalf("could not create test client: %s", err) | |
| } | |
| t.Run("200 response", func(t *testing.T) { | |
| manifest, err := client.FetchManifest(time.Time{}) | |
| if err != nil { | |
| t.Fatal("expected no error") | |
| } | |
| if manifest == nil { | |
| t.Fatal("expected manifest") | |
| } | |
| if expected := "0.1.0"; manifest.Version != expected { | |
| t.Errorf("expected ProductVersion %q, got %q", expected, manifest.Version) | |
| } | |
| }) | |
| t.Run("304 response", func(t *testing.T) { | |
| manifest, err := client.FetchManifest(testManifestLastModified) | |
| if err != nil { | |
| t.Fatal("expected no error") | |
| } | |
| if manifest != nil { | |
| t.Fatalf("expected nil manifest, got %#v", manifest) | |
| } | |
| }) | |
| } | |
| func TestCloudPluginClient_NotSupportedByTerraformCloud(t *testing.T) { | |
| server, err := newHTTPTestServerUnsupported(t) | |
| if err != nil { | |
| t.Fatalf("could not create test server: %s", err) | |
| } | |
| defer server.Close() | |
| serverURL, _ := url.Parse(server.URL) | |
| serviceURL := serverURL.JoinPath("/api/cloudplugin/v1") | |
| client, err := NewCloudPluginClient(context.Background(), serviceURL) | |
| if err != nil { | |
| t.Fatalf("could not create test client: %s", err) | |
| } | |
| _, err = client.FetchManifest(time.Time{}) | |
| if !errors.Is(err, ErrPluginNotSupported) { | |
| t.Errorf("Expected ErrCloudPluginNotSupported, got %v", err) | |
| } | |
| } | |
| func TestRelease_PrimarySHASumsSignatureURL(t *testing.T) { | |
| example := Release{ | |
| URLSHASumsSignatures: []string{ | |
| "https://releases.hashicorp.com/terraform-cloudplugin/0.1.0-prototype/terraform-cloudplugin_0.1.0-prototype_SHA256SUMS.sig", | |
| "https://releases.hashicorp.com/terraform-cloudplugin/0.1.0-prototype/terraform-cloudplugin_0.1.0-prototype_SHA256SUMS/72D7468F.sig", // Not quite right | |
| "https://releases.hashicorp.com/terraform-cloudplugin/0.1.0-prototype/terraform-cloudplugin_0.1.0-prototype_SHA256SUMS.72D7468F.sig", | |
| }, | |
| } | |
| url, err := example.PrimarySHASumsSignatureURL() | |
| if err != nil { | |
| t.Fatalf("Expected no error, got %s", err) | |
| } | |
| if url != example.URLSHASumsSignatures[2] { | |
| t.Errorf("Expected URL %q, but got %q", example.URLSHASumsSignatures[2], url) | |
| } | |
| } | |
| func TestRelease_PrimarySHASumsSignatureURL_lowercase_should_match(t *testing.T) { | |
| example := Release{ | |
| URLSHASumsSignatures: []string{ | |
| "https://releases.hashicorp.com/terraform-cloudplugin/0.1.0-prototype/terraform-cloudplugin_0.1.0-prototype_SHA256SUMS.sig", | |
| "https://releases.hashicorp.com/terraform-cloudplugin/0.1.0-prototype/terraform-cloudplugin_0.1.0-prototype_SHA256SUMS.72d7468f.sig", | |
| }, | |
| } | |
| url, err := example.PrimarySHASumsSignatureURL() | |
| if err != nil { | |
| t.Fatalf("Expected no error, got %s", err) | |
| } | |
| // Not expected but technically fine since these are hex values | |
| if url != example.URLSHASumsSignatures[1] { | |
| t.Errorf("Expected URL %q, but got %q", example.URLSHASumsSignatures[1], url) | |
| } | |
| } | |
| func TestRelease_PrimarySHASumsSignatureURL_no_known_keys(t *testing.T) { | |
| example := Release{ | |
| URLSHASumsSignatures: []string{ | |
| "https://releases.hashicorp.com/terraform-cloudplugin/0.1.0-prototype/terraform-cloudplugin_0.1.0-prototype_SHA256SUMS.sig", | |
| "https://releases.hashicorp.com/terraform-cloudplugin/0.1.0-prototype/terraform-cloudplugin_0.1.0-prototype_SHA256SUMS.ABCDEF012.sig", | |
| }, | |
| } | |
| url, err := example.PrimarySHASumsSignatureURL() | |
| if err != nil { | |
| t.Fatalf("Expected no error, got %s", err) | |
| } | |
| // Returns key with no ID | |
| if url != example.URLSHASumsSignatures[0] { | |
| t.Errorf("Expected URL %q, but got %q", example.URLSHASumsSignatures[0], url) | |
| } | |
| } |