Skip to content

Commit

Permalink
chore: add unit test for getNamespaces
Browse files Browse the repository at this point in the history
  • Loading branch information
afdesk committed Dec 4, 2024
1 parent eed731d commit 75fd906
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
golang.org/x/sync v0.7.0 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
)
Expand Down
210 changes: 210 additions & 0 deletions pkg/trivyk8s/trivyk8s_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,224 @@
package trivyk8s

import (
"context"
"fmt"
"testing"

"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"

"github.com/stretchr/testify/assert"

"github.com/aquasecurity/trivy-kubernetes/pkg/artifacts"
"github.com/aquasecurity/trivy-kubernetes/pkg/bom"
"github.com/aquasecurity/trivy-kubernetes/pkg/k8s"
"github.com/aquasecurity/trivy-kubernetes/pkg/k8s/docker"
)

type MockClusterDynamicClient struct {
resource dynamic.NamespaceableResourceInterface
}

func (m MockClusterDynamicClient) Resource(schema.GroupVersionResource) dynamic.NamespaceableResourceInterface {
return m.resource

}

type MockNamespaceableResourceInterface struct {
err error
namespaces []string
}

func (m MockNamespaceableResourceInterface) Namespace(s string) dynamic.ResourceInterface {
panic("implement me")
}

func (m MockNamespaceableResourceInterface) Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
panic("implement me")
}

func (m MockNamespaceableResourceInterface) Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
panic("implement me")
}

func (m MockNamespaceableResourceInterface) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error) {
panic("implement me")
}

func (m MockNamespaceableResourceInterface) Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error {
panic("implement me")
}

func (m MockNamespaceableResourceInterface) DeleteCollection(ctx context.Context, options metav1.DeleteOptions, listOptions metav1.ListOptions) error {
panic("implement me")
}

func (m MockNamespaceableResourceInterface) Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
panic("implement me")
}

func (m MockNamespaceableResourceInterface) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
panic("implement me")
}

func (m MockNamespaceableResourceInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, options metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) {
panic("implement me")
}

func (m MockNamespaceableResourceInterface) Apply(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error) {
panic("implement me")
}

func (m MockNamespaceableResourceInterface) ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions) (*unstructured.Unstructured, error) {
panic("implement me")
}

func (m MockNamespaceableResourceInterface) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
if m.err != nil {
return nil, m.err
}
result := &unstructured.UnstructuredList{}
for _, namespace := range m.namespaces {
result.Items = append(result.Items, unstructured.Unstructured{
Object: map[string]interface{}{
"metadata": map[string]interface{}{
"name": namespace,
},
},
})
}
return result, nil
}

type MockCluster struct {
dynamicClient dynamic.Interface
}

func newMockCluster(dynamicClient dynamic.Interface) *MockCluster {
return &MockCluster{
dynamicClient: dynamicClient,
}
}

// GetDynamicClient возвращает dynamic.Interface
func (m *MockCluster) GetDynamicClient() dynamic.Interface {
return m.dynamicClient
}

// Stub methods to satisfy the Cluster interface
func (m *MockCluster) GetCurrentContext() string { return "" }
func (m *MockCluster) GetCurrentNamespace() string { return "" }
func (m *MockCluster) GetK8sClientSet() *kubernetes.Clientset { return nil }
func (m *MockCluster) GetGVRs(bool, []string) ([]schema.GroupVersionResource, error) { return nil, nil }
func (m *MockCluster) GetGVR(string) (schema.GroupVersionResource, error) {
return schema.GroupVersionResource{}, nil
}
func (m *MockCluster) CreateClusterBom(ctx context.Context) (*bom.Result, error) { return nil, nil }
func (m *MockCluster) GetClusterVersion() string { return "" }
func (m *MockCluster) AuthByResource(resource unstructured.Unstructured) (map[string]docker.Auth, error) {
return nil, nil
}
func (m *MockCluster) Platform() k8s.Platform { return k8s.Platform{} }

func TestGetNamespaces(t *testing.T) {
tests := []struct {
name string
includeNamespaces []string
excludeNamespaces []string
mockNamespaces []string
mockError error
expectedNamespaces []string
expectedError error
}{
{
name: "No includeNamespaces, no excludeNamespaces",
includeNamespaces: nil,
excludeNamespaces: nil,
mockNamespaces: nil,
expectedNamespaces: []string{},
expectedError: nil,
},
{
name: "Include namespaces set",
includeNamespaces: []string{"namespace1", "namespace2"},
excludeNamespaces: nil,
mockNamespaces: nil,
expectedNamespaces: []string{"namespace1", "namespace2"},
expectedError: nil,
},
{
name: "Exclude namespaces set but no namespaces in cluster",
includeNamespaces: nil,
excludeNamespaces: []string{"namespace3"},
mockNamespaces: nil,
expectedNamespaces: []string{},
expectedError: nil,
},
{
name: "Exclude namespaces set with namespaces in cluster",
includeNamespaces: nil,
excludeNamespaces: []string{"namespace3"},
mockNamespaces: []string{"namespace1", "namespace2", "namespace3"},
expectedNamespaces: []string{"namespace1", "namespace2"},
expectedError: nil,
},
{
name: "Error in listing namespaces",
includeNamespaces: nil,
excludeNamespaces: []string{"namespace3"},
mockError: fmt.Errorf("some error"),
expectedNamespaces: []string{},
expectedError: fmt.Errorf("unable to list namespaces: %v", fmt.Errorf("some error")),
},
{
name: "Forbidden error",
includeNamespaces: nil,
excludeNamespaces: []string{"namespace3"},
mockError: errors.NewForbidden(schema.GroupResource{
Group: "",
Resource: "namespaces",
}, "namespaces", fmt.Errorf("forbidden")),
expectedNamespaces: []string{},
expectedError: fmt.Errorf("'exclude namespaces' option requires a cluster role with permissions to list namespaces"),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create your client with the mocked dependencies
client := &client{
includeNamespaces: tt.includeNamespaces,
excludeNamespaces: tt.excludeNamespaces,
cluster: newMockCluster(MockClusterDynamicClient{
resource: MockNamespaceableResourceInterface{
err: tt.mockError,
namespaces: tt.mockNamespaces,
},
}),
}

// Run the test
namespaces, err := client.getNamespaces()

// Assert the expected values
assert.ElementsMatch(t, namespaces, tt.expectedNamespaces)

if tt.expectedError != nil {
assert.EqualError(t, err, tt.expectedError.Error())
} else {
assert.NoError(t, err)
}
})
}
}

func TestIgnoreNodeByLabel(t *testing.T) {
tests := []struct {
name string
Expand Down

0 comments on commit 75fd906

Please sign in to comment.