Просмотр исходного кода

pod test finalized

Signed-off-by: Sean Holcomb <seanholcomb@gmail.com>
Sean Holcomb 1 месяц назад
Родитель
Сommit
924dcac4a6

+ 3 - 3
core/pkg/model/kubemodel/cluster.go

@@ -32,9 +32,9 @@ func (c *Cluster) ValidateCluster(window Window) error {
 }
 
 func (kms *KubeModelSet) RegisterCluster(cluster *Cluster) error {
-	err := cluster.ValidateCluster(kms.Window)
-	if err != nil {
-		kms.Errorf("RegisterCluster: invalid cluster: %w", err)
+	if err := cluster.ValidateCluster(kms.Window); err != nil {
+		err = fmt.Errorf("RegisterCluster: invalid cluster: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/container.go

@@ -41,7 +41,8 @@ func (c *Container) ValidateContainer(window Window) error {
 
 func (kms *KubeModelSet) RegisterContainer(container *Container) error {
 	if err := container.ValidateContainer(kms.Window); err != nil {
-		kms.Errorf("RegisterContainer: invalid container: %w", err)
+		err = fmt.Errorf("RegisterContainer: invalid container: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/cronjob.go

@@ -39,7 +39,8 @@ func (c *CronJob) ValidateCronJob(window Window) error {
 
 func (kms *KubeModelSet) RegisterCronJob(cronJob *CronJob) error {
 	if err := cronJob.ValidateCronJob(kms.Window); err != nil {
-		kms.Errorf("RegisterCronJob: invalid cronjob: %w", err)
+		err = fmt.Errorf("RegisterCronJob: invalid cronjob: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/daemonset.go

@@ -39,7 +39,8 @@ func (d *DaemonSet) ValidateDaemonSet(window Window) error {
 
 func (kms *KubeModelSet) RegisterDaemonSet(daemonSet *DaemonSet) error {
 	if err := daemonSet.ValidateDaemonSet(kms.Window); err != nil {
-		kms.Errorf("RegisterDaemonSet: invalid daemonset: %w", err)
+		err = fmt.Errorf("RegisterDaemonSet: invalid daemonset: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/dcgm.go

@@ -47,7 +47,8 @@ func (d *DCGMDevice) ValidateDCGMDevice(window Window) error {
 // RegisterDCGMDevice validates and adds a DCGMDevice to the set, keyed by UUID.
 func (kms *KubeModelSet) RegisterDCGMDevice(device *DCGMDevice) error {
 	if err := device.ValidateDCGMDevice(kms.Window); err != nil {
-		kms.Errorf("RegisterDCGMDevice: invalid dcgm device: %w", err)
+		err = fmt.Errorf("RegisterDCGMDevice: invalid dcgm device: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/deployment.go

@@ -40,7 +40,8 @@ func (d *Deployment) ValidateDeployment(window Window) error {
 
 func (kms *KubeModelSet) RegisterDeployment(deployment *Deployment) error {
 	if err := deployment.ValidateDeployment(kms.Window); err != nil {
-		kms.Errorf("RegisterDeployment: invalid deployment: %w", err)
+		err = fmt.Errorf("RegisterDeployment: invalid deployment: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/job.go

@@ -39,7 +39,8 @@ func (j *Job) ValidateJob(window Window) error {
 
 func (kms *KubeModelSet) RegisterJob(job *Job) error {
 	if err := job.ValidateJob(kms.Window); err != nil {
-		kms.Errorf("RegisterJob: invalid job: %w", err)
+		err = fmt.Errorf("RegisterJob: invalid job: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 63 - 0
core/pkg/model/kubemodel/kubemodel_helpers_test.go

@@ -0,0 +1,63 @@
+package kubemodel
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+)
+
+// requireWindowEqual compares two start/end time pairs using time.Time.Equal,
+// which is insensitive to monotonic clock readings and timezone representation.
+func requireWindowEqual(t *testing.T, thisStart, thatStart, thisEnd, thatEnd time.Time) {
+	t.Helper()
+	require.True(t, thisStart.Equal(thatStart), "Start mismatch: %v != %v", thisStart, thatStart)
+	require.True(t, thisEnd.Equal(thatEnd), "End mismatch: %v != %v", thisEnd, thatEnd)
+}
+
+// DiagnosticEquals asserts that two Diagnostics have identical state.
+// Timestamp is excluded from comparison since it is volatile across instances.
+func DiagnosticEquals(t *testing.T, this, that Diagnostic) {
+	t.Helper()
+	require.Equal(t, this.Level, that.Level)
+	require.Equal(t, this.Message, that.Message)
+	require.Equal(t, this.Details, that.Details)
+}
+
+// MetadataEquals asserts that two Metadata structs have identical state.
+// Timestamps (CreatedAt, CompletedAt, Diagnostic.Timestamp) are excluded
+// from comparison since they are volatile across instances.
+func MetadataEquals(t *testing.T, this, that *Metadata) {
+	t.Helper()
+	require.Equal(t, this.ObjectCount, that.ObjectCount)
+	require.Equal(t, this.DiagnosticLevel, that.DiagnosticLevel)
+	require.Equal(t, len(this.Diagnostics), len(that.Diagnostics))
+	for i := range this.Diagnostics {
+		DiagnosticEquals(t, this.Diagnostics[i], that.Diagnostics[i])
+	}
+}
+
+// KubeModelSetEquals asserts that two KubeModelSets have identical state.
+// Metadata timestamps (CreatedAt, CompletedAt, Diagnostic.Timestamp) are
+// excluded from comparison since they are volatile across instances.
+func KubeModelSetEquals(t *testing.T, this, that *KubeModelSet) {
+	t.Helper()
+	require.Equal(t, this.Window, that.Window)
+	require.Equal(t, this.Cluster, that.Cluster)
+	MetadataEquals(t, this.Metadata, that.Metadata)
+	require.Equal(t, this.Namespaces, that.Namespaces)
+	require.Equal(t, this.ResourceQuotas, that.ResourceQuotas)
+	require.Equal(t, this.Containers, that.Containers)
+	require.Equal(t, this.Deployments, that.Deployments)
+	require.Equal(t, this.StatefulSets, that.StatefulSets)
+	require.Equal(t, this.DaemonSets, that.DaemonSets)
+	require.Equal(t, this.Jobs, that.Jobs)
+	require.Equal(t, this.CronJobs, that.CronJobs)
+	require.Equal(t, this.ReplicaSets, that.ReplicaSets)
+	require.Equal(t, this.Nodes, that.Nodes)
+	require.Equal(t, this.Pods, that.Pods)
+	require.Equal(t, this.PersistentVolumeClaims, that.PersistentVolumeClaims)
+	require.Equal(t, this.Services, that.Services)
+	require.Equal(t, this.PersistentVolumes, that.PersistentVolumes)
+	require.Equal(t, this.DCGMDevices, that.DCGMDevices)
+}

+ 64 - 0
core/pkg/model/kubemodel/kubemodel_test.go

@@ -2,12 +2,76 @@ package kubemodel
 
 import (
 	"errors"
+	"fmt"
 	"testing"
 	"time"
 
 	"github.com/stretchr/testify/require"
 )
 
+func TestCheckWindow(t *testing.T) {
+	start := time.Now().UTC().Truncate(time.Hour)
+	end := start.Add(time.Hour)
+	window := Window{Start: start, End: end}
+
+	windowErrMsg := func(s, e time.Time) string {
+		return fmt.Sprintf(
+			"start or end time (%s-%s) is outside of the window %s-%s",
+			s.Format(time.RFC3339),
+			e.Format(time.RFC3339),
+			start.Format(time.RFC3339),
+			end.Format(time.RFC3339),
+		)
+	}
+
+	tests := []struct {
+		name    string
+		start   time.Time
+		end     time.Time
+		wantErr string
+	}{
+		{
+			name:    "start before window",
+			start:   start.Add(-time.Minute),
+			end:     end,
+			wantErr: windowErrMsg(start.Add(-time.Minute), end),
+		},
+		{
+			name:    "end after window",
+			start:   start,
+			end:     end.Add(time.Minute),
+			wantErr: windowErrMsg(start, end.Add(time.Minute)),
+		},
+		{
+			name:    "entirely outside window",
+			start:   end.Add(time.Hour),
+			end:     end.Add(2 * time.Hour),
+			wantErr: windowErrMsg(end.Add(time.Hour), end.Add(2*time.Hour)),
+		},
+		{
+			name:  "exact window boundaries",
+			start: start,
+			end:   end,
+		},
+		{
+			name:  "within window",
+			start: start.Add(15 * time.Minute),
+			end:   end.Add(-15 * time.Minute),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := checkWindow(window, tt.start, tt.end)
+			if tt.wantErr != "" {
+				require.EqualError(t, err, tt.wantErr)
+			} else {
+				require.NoError(t, err)
+			}
+		})
+	}
+}
+
 func TestKubeModel(t *testing.T) {
 	start := time.Now().UTC().Truncate(time.Hour)
 	end := start.Add(time.Hour)

+ 2 - 1
core/pkg/model/kubemodel/namespace.go

@@ -36,7 +36,8 @@ func (n *Namespace) ValidateNamespace(window Window) error {
 
 func (kms *KubeModelSet) RegisterNamespace(namespace *Namespace) error {
 	if err := namespace.ValidateNamespace(kms.Window); err != nil {
-		kms.Errorf("RegisterNamespace: invalid namespace: %w", err)
+		err = fmt.Errorf("RegisterNamespace: invalid namespace: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/node.go

@@ -50,7 +50,8 @@ func (n *Node) ValidateNode(window Window) error {
 // RegisterNode validates and adds a node to the set
 func (kms *KubeModelSet) RegisterNode(node *Node) error {
 	if err := node.ValidateNode(kms.Window); err != nil {
-		kms.Errorf("RegisterNode: invalid node: %w", err)
+		err = fmt.Errorf("RegisterNode: invalid node: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/pod.go

@@ -48,7 +48,8 @@ func (p *Pod) ValidatePod(window Window) error {
 
 func (kms *KubeModelSet) RegisterPod(pod *Pod) error {
 	if err := pod.ValidatePod(kms.Window); err != nil {
-		kms.Errorf("RegisterPod: invalid pod: %w", err)
+		err = fmt.Errorf("RegisterPod: invalid pod: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 148 - 0
core/pkg/model/kubemodel/pod_test.go

@@ -0,0 +1,148 @@
+package kubemodel
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestValidatePod(t *testing.T) {
+	start := time.Now().UTC().Truncate(time.Hour)
+	end := start.Add(time.Hour)
+	window := Window{Start: start, End: end}
+
+	tests := []struct {
+		name    string
+		pod     *Pod
+		wantErr string
+	}{
+		{
+			name:    "empty UID",
+			pod:     &Pod{Name: "my-pod", NamespaceUID: "ns-uid", Start: start, End: end},
+			wantErr: "UID is missing for Pod with name 'my-pod'",
+		},
+		{
+			name:    "empty Name",
+			pod:     &Pod{UID: "pod-uid", NamespaceUID: "ns-uid", Start: start, End: end},
+			wantErr: "Name is missing for Pod 'pod-uid'",
+		},
+		{
+			name:    "empty NamespaceUID",
+			pod:     &Pod{UID: "pod-uid", Name: "my-pod", Start: start, End: end},
+			wantErr: "NamespaceUID is missing for Pod 'pod-uid'",
+		},
+		{
+			name:    "outside window",
+			pod:     &Pod{UID: "pod-uid", Name: "my-pod", NamespaceUID: "ns-uid", Start: start.Add(-time.Hour), End: end},
+			wantErr: checkWindow(window, start.Add(-time.Hour), end).Error(),
+		},
+		{
+			name: "valid",
+			pod:  &Pod{UID: "pod-uid", Name: "my-pod", NamespaceUID: "ns-uid", Start: start, End: end},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := tt.pod.ValidatePod(window)
+			if tt.wantErr != "" {
+				require.EqualError(t, err, tt.wantErr)
+			} else {
+				require.NoError(t, err)
+			}
+		})
+	}
+}
+
+func TestRegisterPod(t *testing.T) {
+	start := time.Now().UTC().Truncate(time.Hour)
+	end := start.Add(time.Hour)
+
+	newPod := func(uid, name string) *Pod {
+		return &Pod{UID: uid, Name: name, NamespaceUID: "ns-uid", Start: start, End: end}
+	}
+	withCluster := func(kms *KubeModelSet) {
+		kms.RegisterCluster(&Cluster{UID: "cluster-uid", Start: start, End: end})
+	}
+
+	tests := []struct {
+		name    string
+		setup   func(*KubeModelSet)
+		pod     *Pod
+		wantErr string
+		want    *KubeModelSet
+	}{
+		{
+			name:    "validation failure",
+			pod:     &Pod{UID: "", Name: "my-pod", NamespaceUID: "ns-uid", Start: start, End: end},
+			wantErr: "RegisterPod: invalid pod: UID is missing for Pod with name 'my-pod'",
+			want: func() *KubeModelSet {
+				kms := NewKubeModelSet(start, end)
+				kms.Metadata.Diagnostics = []Diagnostic{
+					{Level: DiagnosticLevelError, Message: "RegisterPod: invalid pod: UID is missing for Pod with name 'my-pod'"},
+				}
+				return kms
+			}(),
+		},
+		{
+			name: "warns when cluster is nil",
+			pod:  newPod("pod-uid", "my-pod"),
+			want: func() *KubeModelSet {
+				kms := NewKubeModelSet(start, end)
+				kms.Pods["pod-uid"] = newPod("pod-uid", "my-pod")
+				kms.Metadata.ObjectCount = 1
+				kms.Metadata.Diagnostics = []Diagnostic{
+					{Level: DiagnosticLevelWarning, Message: "RegisterPod: Cluster is nil"},
+				}
+				return kms
+			}(),
+		},
+		{
+			name:  "registers pod with cluster",
+			setup: withCluster,
+			pod:   newPod("pod-uid", "my-pod"),
+			want: func() *KubeModelSet {
+				kms := NewKubeModelSet(start, end)
+				withCluster(kms)
+				kms.Pods["pod-uid"] = newPod("pod-uid", "my-pod")
+				kms.Metadata.ObjectCount = 1
+				return kms
+			}(),
+		},
+		{
+			name: "duplicate registration is a no-op",
+			setup: func(kms *KubeModelSet) {
+				withCluster(kms)
+				kms.RegisterPod(newPod("pod-uid", "original"))
+			},
+			pod: newPod("pod-uid", "duplicate"),
+			want: func() *KubeModelSet {
+				kms := NewKubeModelSet(start, end)
+				withCluster(kms)
+				kms.Pods["pod-uid"] = newPod("pod-uid", "original")
+				kms.Metadata.ObjectCount = 1
+				return kms
+			}(),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			if tt.setup != nil {
+				tt.setup(kms)
+			}
+
+			err := kms.RegisterPod(tt.pod)
+
+			if tt.wantErr != "" {
+				require.EqualError(t, err, tt.wantErr)
+			} else {
+				require.NoError(t, err)
+			}
+
+			KubeModelSetEquals(t, tt.want, kms)
+		})
+	}
+}

+ 2 - 1
core/pkg/model/kubemodel/pv.go

@@ -34,7 +34,8 @@ func (p *PersistentVolume) ValidatePersistentVolume(window Window) error {
 
 func (kms *KubeModelSet) RegisterPersistentVolume(pv *PersistentVolume) error {
 	if err := pv.ValidatePersistentVolume(kms.Window); err != nil {
-		kms.Errorf("RegisterPersistentVolume: invalid persistent volume: %w", err)
+		err = fmt.Errorf("RegisterPersistentVolume: invalid persistent volume: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/pvc.go

@@ -41,7 +41,8 @@ func (p *PersistentVolumeClaim) ValidatePVC(window Window) error {
 
 func (kms *KubeModelSet) RegisterPVC(pvc *PersistentVolumeClaim) error {
 	if err := pvc.ValidatePVC(kms.Window); err != nil {
-		kms.Errorf("RegisterPVC: invalid pvc: %w", err)
+		err = fmt.Errorf("RegisterPVC: invalid pvc: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/replicaset.go

@@ -40,7 +40,8 @@ func (r *ReplicaSet) ValidateReplicaSet(window Window) error {
 
 func (kms *KubeModelSet) RegisterReplicaSet(replicaSet *ReplicaSet) error {
 	if err := replicaSet.ValidateReplicaSet(kms.Window); err != nil {
-		kms.Errorf("RegisterReplicaSet: invalid replicaset: %w", err)
+		err = fmt.Errorf("RegisterReplicaSet: invalid replicaset: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/resourcequota.go

@@ -92,7 +92,8 @@ func (rq *ResourceQuota) ValidateResourceQuota(window Window) error {
 
 func (kms *KubeModelSet) RegisterResourceQuota(resourceQuota *ResourceQuota) error {
 	if err := resourceQuota.ValidateResourceQuota(kms.Window); err != nil {
-		kms.Errorf("RegisterResourceQuota: invalid resource quota: %w", err)
+		err = fmt.Errorf("RegisterResourceQuota: invalid resource quota: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/service.go

@@ -69,7 +69,8 @@ func (s *Service) ValidateService(window Window) error {
 
 func (kms *KubeModelSet) RegisterService(service *Service) error {
 	if err := service.ValidateService(kms.Window); err != nil {
-		kms.Errorf("RegisterService: invalid service: %w", err)
+		err = fmt.Errorf("RegisterService: invalid service: %w", err)
+		kms.Error(err)
 		return err
 	}
 

+ 2 - 1
core/pkg/model/kubemodel/statefulset.go

@@ -40,7 +40,8 @@ func (s *StatefulSet) ValidateStatefulSet(window Window) error {
 
 func (kms *KubeModelSet) RegisterStatefulSet(statefulSet *StatefulSet) error {
 	if err := statefulSet.ValidateStatefulSet(kms.Window); err != nil {
-		kms.Errorf("RegisterStatefulSet: invalid statefulset: %w", err)
+		err = fmt.Errorf("RegisterStatefulSet: invalid statefulset: %w", err)
+		kms.Error(err)
 		return err
 	}