From b411b5e6a321650eb0a19edd8910da85be2ce079 Mon Sep 17 00:00:00 2001 From: shipengqi Date: Sat, 16 Sep 2023 17:55:09 +0800 Subject: [PATCH] add unit tests for multisecrets Signed-off-by: shipengqi --- pkg/storage/driver/mock_test.go | 86 +++++++++ pkg/storage/driver/multisecrets_test.go | 241 ++++++++++++++++++++++++ 2 files changed, 327 insertions(+) create mode 100644 pkg/storage/driver/multisecrets_test.go diff --git a/pkg/storage/driver/mock_test.go b/pkg/storage/driver/mock_test.go index 81bda9324..bff3d37ca 100644 --- a/pkg/storage/driver/mock_test.go +++ b/pkg/storage/driver/mock_test.go @@ -252,6 +252,92 @@ func (mock *MockSecretsInterface) Delete(_ context.Context, name string, _ metav return nil } +// newTestFixtureMultiSecrets initializes a MockMultiSecretsInterface. +// MultiSecrets are created for each release provided. +func newTestFixtureMultiSecrets(t *testing.T, releases ...*rspb.Release) *MultiSecrets { + var mock MockMultiSecretsInterface + mock.Init(t, releases...) + + return NewMultiSecrets(&mock) +} + +// MockMultiSecretsInterface mocks a kubernetes SecretsInterface +type MockMultiSecretsInterface struct { + corev1.SecretInterface + + objects map[string]*v1.Secret +} + +// Init initializes the MockMultiSecretsInterface with the set of releases. +func (mock *MockMultiSecretsInterface) Init(t *testing.T, releases ...*rspb.Release) { + mock.objects = map[string]*v1.Secret{} + + for _, rls := range releases { + objkey := testKey(rls.Name, rls.Version) + + secrets, err := newMultiSecretsObject(objkey, rls, nil, -1) + if err != nil { + t.Fatalf("Failed to create secrets: %s", err) + } + mock.objects[objkey] = &(*secrets)[0] + } +} + +// Get returns the Secret by name. +func (mock *MockMultiSecretsInterface) Get(_ context.Context, name string, _ metav1.GetOptions) (*v1.Secret, error) { + object, ok := mock.objects[name] + if !ok { + return nil, apierrors.NewNotFound(v1.Resource("tests"), name) + } + return object, nil +} + +// List returns the a of Secret. +func (mock *MockMultiSecretsInterface) List(_ context.Context, opts metav1.ListOptions) (*v1.SecretList, error) { + var list v1.SecretList + + labelSelector, err := kblabels.Parse(opts.LabelSelector) + if err != nil { + return nil, err + } + + for _, secret := range mock.objects { + if labelSelector.Matches(kblabels.Set(secret.ObjectMeta.Labels)) { + list.Items = append(list.Items, *secret) + } + } + return &list, nil +} + +// Create creates a new Secret. +func (mock *MockMultiSecretsInterface) Create(_ context.Context, secret *v1.Secret, _ metav1.CreateOptions) (*v1.Secret, error) { + name := secret.ObjectMeta.Name + if object, ok := mock.objects[name]; ok { + return object, apierrors.NewAlreadyExists(v1.Resource("tests"), name) + } + mock.objects[name] = secret + return secret, nil +} + +// Update updates a Secret. +func (mock *MockMultiSecretsInterface) Update(_ context.Context, secret *v1.Secret, _ metav1.UpdateOptions) (*v1.Secret, error) { + name := secret.ObjectMeta.Name + if _, ok := mock.objects[name]; !ok { + return nil, apierrors.NewNotFound(v1.Resource("tests"), name) + } + mock.objects[name] = secret + return secret, nil +} + +// Delete deletes a Secret by name. +func (mock *MockMultiSecretsInterface) Delete(_ context.Context, name string, _ metav1.DeleteOptions) error { + if _, ok := mock.objects[name]; !ok { + return apierrors.NewNotFound(v1.Resource("tests"), name) + } + delete(mock.objects, name) + return nil +} + // newTestFixtureSQL mocks the SQL database (for testing purposes) func newTestFixtureSQL(t *testing.T, _ ...*rspb.Release) (*SQL, sqlmock.Sqlmock) { sqlDB, mock, err := sqlmock.New() diff --git a/pkg/storage/driver/multisecrets_test.go b/pkg/storage/driver/multisecrets_test.go new file mode 100644 index 000000000..b4d8aa88a --- /dev/null +++ b/pkg/storage/driver/multisecrets_test.go @@ -0,0 +1,241 @@ +/* +Copyright The Helm Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package driver + +import ( + "encoding/base64" + "encoding/json" + "reflect" + "testing" + + v1 "k8s.io/api/core/v1" + + rspb "helm.sh/helm/v3/pkg/release" +) + +func TestMultiSecretsName(t *testing.T) { + c := newTestFixtureMultiSecrets(t) + if c.Name() != MultiSecretsDriverName { + t.Errorf("Expected name to be %q, got %q", MultiSecretsDriverName, c.Name()) + } +} + +func TestMultiSecretsGet(t *testing.T) { + vers := 1 + name := "smug-pigeon" + namespace := "default" + key := testKey(name, vers) + rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + + secrets := newTestFixtureMultiSecrets(t, []*rspb.Release{rel}...) + + // get release with key + got, err := secrets.Get(key) + if err != nil { + t.Fatalf("Failed to get release: %s", err) + } + // compare fetched release with original + if !reflect.DeepEqual(rel, got) { + t.Errorf("Expected {%v}, got {%v}", rel, got) + } +} + +func TestUNcompressedMultiSecretsGet(t *testing.T) { + vers := 1 + name := "smug-pigeon" + namespace := "default" + key := testKey(name, vers) + rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + + // Create a test fixture which contains an uncompressed release + secret, err := newSecretsObject(key, rel, nil) + if err != nil { + t.Fatalf("Failed to create secret: %s", err) + } + b, err := json.Marshal(rel) + if err != nil { + t.Fatalf("Failed to marshal release: %s", err) + } + secret.Data["release"] = []byte(base64.StdEncoding.EncodeToString(b)) + var mock MockSecretsInterface + mock.objects = map[string]*v1.Secret{key: secret} + secrets := NewSecrets(&mock) + + // get release with key + got, err := secrets.Get(key) + if err != nil { + t.Fatalf("Failed to get release: %s", err) + } + // compare fetched release with original + if !reflect.DeepEqual(rel, got) { + t.Errorf("Expected {%v}, got {%v}", rel, got) + } +} + +func TestMultiSecretsList(t *testing.T) { + secrets := newTestFixtureMultiSecrets(t, []*rspb.Release{ + releaseStub("key-1", 1, "default", rspb.StatusUninstalled), + releaseStub("key-2", 1, "default", rspb.StatusUninstalled), + releaseStub("key-3", 1, "default", rspb.StatusDeployed), + releaseStub("key-4", 1, "default", rspb.StatusDeployed), + releaseStub("key-5", 1, "default", rspb.StatusSuperseded), + releaseStub("key-6", 1, "default", rspb.StatusSuperseded), + }...) + + // list all deleted releases + del, err := secrets.List(func(rel *rspb.Release) bool { + return rel.Info.Status == rspb.StatusUninstalled + }) + // check + if err != nil { + t.Errorf("Failed to list deleted: %s", err) + } + if len(del) != 2 { + t.Errorf("Expected 2 deleted, got %d:\n%v\n", len(del), del) + } + + // list all deployed releases + dpl, err := secrets.List(func(rel *rspb.Release) bool { + return rel.Info.Status == rspb.StatusDeployed + }) + // check + if err != nil { + t.Errorf("Failed to list deployed: %s", err) + } + if len(dpl) != 2 { + t.Errorf("Expected 2 deployed, got %d", len(dpl)) + } + + // list all superseded releases + ssd, err := secrets.List(func(rel *rspb.Release) bool { + return rel.Info.Status == rspb.StatusSuperseded + }) + // check + if err != nil { + t.Errorf("Failed to list superseded: %s", err) + } + if len(ssd) != 2 { + t.Errorf("Expected 2 superseded, got %d", len(ssd)) + } +} + +func TestMultiSecretsQuery(t *testing.T) { + secrets := newTestFixtureMultiSecrets(t, []*rspb.Release{ + releaseStub("key-1", 1, "default", rspb.StatusUninstalled), + releaseStub("key-2", 1, "default", rspb.StatusUninstalled), + releaseStub("key-3", 1, "default", rspb.StatusDeployed), + releaseStub("key-4", 1, "default", rspb.StatusDeployed), + releaseStub("key-5", 1, "default", rspb.StatusSuperseded), + releaseStub("key-6", 1, "default", rspb.StatusSuperseded), + }...) + + rls, err := secrets.Query(map[string]string{"status": "deployed"}) + if err != nil { + t.Fatalf("Failed to query: %s", err) + } + if len(rls) != 2 { + t.Fatalf("Expected 2 results, actual %d", len(rls)) + } + + _, err = secrets.Query(map[string]string{"name": "notExist"}) + if err != ErrReleaseNotFound { + t.Errorf("Expected {%v}, got {%v}", ErrReleaseNotFound, err) + } +} + +func TestMultiSecretsCreate(t *testing.T) { + secrets := newTestFixtureMultiSecrets(t) + + vers := 1 + name := "smug-pigeon" + namespace := "default" + key := testKey(name, vers) + rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + + // store the release in a secret + if err := secrets.Create(key, rel); err != nil { + t.Fatalf("Failed to create release with key %q: %s", key, err) + } + + // get the release back + got, err := secrets.Get(key) + if err != nil { + t.Fatalf("Failed to get release with key %q: %s", key, err) + } + + // compare created release with original + if !reflect.DeepEqual(rel, got) { + t.Errorf("Expected {%v}, got {%v}", rel, got) + } +} + +func TestMultiSecretsUpdate(t *testing.T) { + vers := 1 + name := "smug-pigeon" + namespace := "default" + key := testKey(name, vers) + rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + + secrets := newTestFixtureMultiSecrets(t, []*rspb.Release{rel}...) + + // modify release status code + rel.Info.Status = rspb.StatusSuperseded + + // perform the update + if err := secrets.Update(key, rel); err != nil { + t.Fatalf("Failed to update release: %s", err) + } + + // fetch the updated release + got, err := secrets.Get(key) + if err != nil { + t.Fatalf("Failed to get release with key %q: %s", key, err) + } + + // check release has actually been updated by comparing modified fields + if rel.Info.Status != got.Info.Status { + t.Errorf("Expected status %s, got status %s", rel.Info.Status.String(), got.Info.Status.String()) + } +} + +func TestMultiSecretsDelete(t *testing.T) { + vers := 1 + name := "smug-pigeon" + namespace := "default" + key := testKey(name, vers) + rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) + + secrets := newTestFixtureMultiSecrets(t, []*rspb.Release{rel}...) + + // perform the delete on a non-existing release + _, err := secrets.Delete("nonexistent") + if err != ErrReleaseNotFound { + t.Fatalf("Expected ErrReleaseNotFound, got: {%v}", err) + } + + // perform the delete + rls, err := secrets.Delete(key) + if err != nil { + t.Fatalf("Failed to delete release with key %q: %s", key, err) + } + if !reflect.DeepEqual(rel, rls) { + t.Errorf("Expected {%v}, got {%v}", rel, rls) + } + + // fetch the deleted release + _, err = secrets.Get(key) + if !reflect.DeepEqual(ErrReleaseNotFound, err) { + t.Errorf("Expected {%v}, got {%v}", ErrReleaseNotFound, err) + } +}