Przeglądaj źródła

Update kubemodel tests

Signed-off-by: Sean Holcomb <seanholcomb@gmail.com>
Sean Holcomb 2 dni temu
rodzic
commit
da94d553aa
1 zmienionych plików z 170 dodań i 328 usunięć
  1. 170 328
      core/pkg/compute/kubemodel/kubemodel_test.go

+ 170 - 328
core/pkg/compute/kubemodel/kubemodel_test.go

@@ -9,7 +9,6 @@ import (
 
 	"github.com/opencost/opencost/core/pkg/model/kubemodel"
 	"github.com/opencost/opencost/core/pkg/source"
-	"github.com/opencost/opencost/core/pkg/util"
 )
 
 const testClusterUID = "cluster-uid-1"
@@ -19,15 +18,6 @@ func newTestWindow() (time.Time, time.Time) {
 	return start, start.Add(time.Hour)
 }
 
-func newTestKubeModel(t *testing.T) (*KubeModel, *source.MockOpenCostDataSource) {
-	t.Helper()
-	ds := source.NewMockOpenCostDataSource()
-	ds.ResolutionValue = 5 * time.Minute
-	km, err := NewKubeModel(testClusterUID, ds)
-	require.NoError(t, err)
-	return km, ds
-}
-
 // seedCluster sets the minimum overrides needed for computeCluster to succeed.
 func seedCluster(ds *source.MockOpenCostDataSource, start, end time.Time) {
 	ds.Querier.SetOverride(source.QueryClusterInfo, []*source.ClusterInfoResult{
@@ -45,327 +35,179 @@ func TestNewKubeModel_NilDataSource(t *testing.T) {
 	require.Error(t, err)
 }
 
-// ---- computeCluster ----
-
-func TestComputeCluster_HappyPath(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-	require.NotNil(t, kms.Cluster)
-	assert.Equal(t, testClusterUID, kms.Cluster.UID)
-	assert.Equal(t, "my-cluster", kms.Cluster.Name)
-}
-
-func TestComputeCluster_UIDNotFound(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-
-	ds.Querier.SetOverride(source.QueryClusterInfo, []*source.ClusterInfoResult{
-		{UID: "other-uid", Cluster: "other-cluster"},
-	})
-
-	_, err := km.ComputeKubeModelSet(start, end)
-	require.Error(t, err)
-	assert.Contains(t, err.Error(), testClusterUID)
-}
-
-func TestComputeCluster_NoUptime(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-
-	ds.Querier.SetOverride(source.QueryClusterInfo, []*source.ClusterInfoResult{
-		{UID: testClusterUID, Cluster: "my-cluster"},
-	})
-	// QueryClusterUptime left at NoOp — cluster Start/End stay zero, fail window
-	// validation inside RegisterCluster, so kms.Cluster is not set.
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-	assert.Nil(t, kms.Cluster)
-}
-
-// ---- computeNodes ----
-
-func TestComputeNodes_HappyPath(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
-
-	ds.Querier.SetOverride(source.QueryNodeInfo, []*source.NodeInfoResult{
-		{UID: "node-1", Node: "node-a", ProviderID: "aws:///us-east-1a/i-abc"},
-		{UID: "node-2", Node: "node-b", ProviderID: "aws:///us-east-1b/i-def"},
-	})
-	ds.Querier.SetOverride(source.QueryNodeUptime, []*source.UptimeResult{
-		{UID: "node-1", First: start, Last: end},
-		{UID: "node-2", First: start, Last: end},
-	})
-	ds.Querier.SetOverride(source.QueryNodeLabels, []*source.NodeLabelsResult{
-		{UID: "node-1", Labels: map[string]string{"zone": "us-east-1a"}},
-	})
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-
-	assert.Len(t, kms.Nodes, 2)
-	n1 := kms.Nodes["node-1"]
-	require.NotNil(t, n1)
-	assert.Equal(t, "node-a", n1.Name)
-	assert.Equal(t, "aws:///us-east-1a/i-abc", n1.ProviderID)
-	assert.Equal(t, map[string]string{"zone": "us-east-1a"}, n1.Labels)
-}
-
-func TestComputeNodes_ResourceCapacities(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
-
-	ds.Querier.SetOverride(source.QueryNodeInfo, []*source.NodeInfoResult{
-		{UID: "node-1", Node: "node-a"},
-	})
-	ds.Querier.SetOverride(source.QueryNodeUptime, []*source.UptimeResult{
-		{UID: "node-1", First: start, Last: end},
-	})
-	ds.Querier.SetOverride(source.QueryNodeResourceCapacities, []*source.ResourceResult{
-		{UID: "node-1", Resource: "cpu", Unit: "cores", Value: 4.0},
-		{UID: "node-1", Resource: "memory", Unit: "bytes", Value: 16 * 1024 * 1024 * 1024},
-	})
-	ds.Querier.SetOverride(source.QueryNodeResourcesAllocatable, []*source.ResourceResult{
-		{UID: "node-1", Resource: "cpu", Unit: "cores", Value: 3.9},
-	})
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-
-	n := kms.Nodes["node-1"]
-	require.NotNil(t, n)
-	assert.NotEmpty(t, n.ResourceCapacities)
-	assert.NotEmpty(t, n.ResourcesAllocatable)
-}
-
-func TestComputeNodes_LocalStorage(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
-
-	ds.Querier.SetOverride(source.QueryNodeInfo, []*source.NodeInfoResult{
-		{UID: "node-1", Node: "node-a"},
-	})
-	ds.Querier.SetOverride(source.QueryNodeUptime, []*source.UptimeResult{
-		{UID: "node-1", First: start, Last: end},
-	})
-	ds.Querier.SetOverride(source.QueryKMLocalStorageBytes, []*source.UIDValueResult{
-		{UID: "node-1", Value: 500 * 1024 * 1024 * 1024},
-	})
-	ds.Querier.SetOverride(source.QueryKMLocalStorageUsedAvg, []*source.NodeUIDValueResult{
-		{UID: "node-1", Value: 100 * 1024 * 1024 * 1024},
-	})
-	ds.Querier.SetOverride(source.QueryKMLocalStorageUsedMax, []*source.NodeUIDValueResult{
-		{UID: "node-1", Value: 200 * 1024 * 1024 * 1024},
-	})
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-
-	n := kms.Nodes["node-1"]
-	require.NotNil(t, n)
-	assert.Equal(t, float64(500*1024*1024*1024), n.FileSystem.CapacityBytes)
-	assert.Equal(t, float64(100*1024*1024*1024), n.FileSystem.UsageByteAvg)
-	assert.Equal(t, float64(200*1024*1024*1024), n.FileSystem.UsageByteMax)
-}
-
-func TestComputeNodes_EmptyResults(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-	assert.Empty(t, kms.Nodes)
-}
-
-// ---- computePods ----
-
-func TestComputePods_HappyPath(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
-
-	ds.Querier.SetOverride(source.QueryPodInfo, []*source.PodInfoResult{
-		{UID: "pod-1", Pod: "my-pod", NamespaceUID: "ns-1", NodeUID: "node-1"},
-	})
-	ds.Querier.SetOverride(source.QueryPodUptime, []*source.UptimeResult{
-		{UID: "pod-1", First: start, Last: end},
-	})
-	ds.Querier.SetOverride(source.QueryPodLabels, []*source.PodLabelsResult{
-		{UID: "pod-1", Labels: map[string]string{"app": "my-app"}},
-	})
-	ds.Querier.SetOverride(source.QueryPodAnnotations, []*source.PodAnnotationsResult{
-		{UID: "pod-1", Annotations: map[string]string{"note": "val"}},
-	})
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-
-	pod := kms.Pods["pod-1"]
-	require.NotNil(t, pod)
-	assert.Equal(t, "my-pod", pod.Name)
-	assert.Equal(t, "ns-1", pod.NamespaceUID)
-	assert.Equal(t, "node-1", pod.NodeUID)
-	assert.Equal(t, map[string]string{"app": "my-app"}, pod.Labels)
-	assert.Equal(t, map[string]string{"note": "val"}, pod.Annotations)
-}
-
-func TestComputePods_Owners(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
-
-	ds.Querier.SetOverride(source.QueryPodInfo, []*source.PodInfoResult{
-		{UID: "pod-1", Pod: "my-pod", NamespaceUID: "ns-1"},
-	})
-	ds.Querier.SetOverride(source.QueryPodUptime, []*source.UptimeResult{
-		{UID: "pod-1", First: start, Last: end},
-	})
-	ds.Querier.SetOverride(source.QueryPodOwners, []*source.OwnerResult{
-		{UID: "pod-1", OwnerUID: "rs-1", OwnerKind: "ReplicaSet", Controller: true},
-	})
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-
-	pod := kms.Pods["pod-1"]
-	require.NotNil(t, pod)
-	require.Len(t, pod.Owners, 1)
-	assert.Equal(t, "rs-1", pod.Owners[0].UID)
-	assert.True(t, pod.Owners[0].Controller)
-}
-
-func TestComputePods_PVCVolumes(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
-
-	ds.Querier.SetOverride(source.QueryPodInfo, []*source.PodInfoResult{
-		{UID: "pod-1", Pod: "my-pod", NamespaceUID: "ns-1"},
-	})
-	ds.Querier.SetOverride(source.QueryPodUptime, []*source.UptimeResult{
-		{UID: "pod-1", First: start, Last: end},
-	})
-	ds.Querier.SetOverride(source.QueryPodPVCVolumes, []*source.PodPVCVolumeResult{
-		{UID: "pod-1", PVCUID: "pvc-1", PodVolumeName: "data"},
-	})
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-
-	pod := kms.Pods["pod-1"]
-	require.NotNil(t, pod)
-	require.Len(t, pod.PVCVolumes, 1)
-	assert.Equal(t, "pvc-1", pod.PVCVolumes[0].PersistentVolumeClaimUID)
-	assert.Equal(t, "data", pod.PVCVolumes[0].Name)
-}
-
-// ---- computeContainers ----
-
-func TestComputeContainers_HappyPath(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
-
-	ds.Querier.SetOverride(source.QueryContainerUptime, []*source.ContainerUptimeResult{
-		{UptimeResult: source.UptimeResult{UID: "pod-1", First: start, Last: end}, Container: "app"},
-		{UptimeResult: source.UptimeResult{UID: "pod-1", First: start, Last: end}, Container: "sidecar"},
-	})
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-	assert.Len(t, kms.Containers, 2)
-}
-
-func TestComputeContainers_ResourceRequestsAndLimits(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
+// ---- ComputeKubeModelSet orchestration ----
 
-	ds.Querier.SetOverride(source.QueryContainerUptime, []*source.ContainerUptimeResult{
-		{UptimeResult: source.UptimeResult{UID: "pod-1", First: start, Last: end}, Container: "app"},
-	})
-	ds.Querier.SetOverride(source.QueryContainerResourceRequests, []*source.ContainerResourceResult{
-		{ResourceResult: source.ResourceResult{UID: "pod-1", Resource: "cpu", Unit: "cores", Value: 0.5}, Container: "app"},
-		{ResourceResult: source.ResourceResult{UID: "pod-1", Resource: "memory", Unit: "bytes", Value: 128 * 1024 * 1024}, Container: "app"},
-	})
-	ds.Querier.SetOverride(source.QueryContainerResourceLimits, []*source.ContainerResourceResult{
-		{ResourceResult: source.ResourceResult{UID: "pod-1", Resource: "cpu", Unit: "cores", Value: 1.0}, Container: "app"},
-	})
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-
-	c := findContainer(kms, "pod-1", "app")
-	require.NotNil(t, c)
-	assert.NotEmpty(t, c.ResourceRequests)
-	assert.NotEmpty(t, c.ResourceLimits)
-}
-
-func TestComputeContainers_CPUAndRAMUsage(t *testing.T) {
+func TestComputeKubeModelSet(t *testing.T) {
 	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
 
-	ts := float64(start.Unix())
-	ds.Querier.SetOverride(source.QueryContainerUptime, []*source.ContainerUptimeResult{
-		{UptimeResult: source.UptimeResult{UID: "pod-1", First: start, Last: end}, Container: "app"},
-	})
-	ds.Querier.SetOverride(source.QueryCPUCoresAllocated, []*source.CPUCoresAllocatedResult{
-		{UID: "pod-1", Container: "app", Data: []*util.Vector{{Timestamp: ts, Value: 0.25}}},
-	})
-	ds.Querier.SetOverride(source.QueryCPUUsageAvg, []*source.CPUUsageAvgResult{
-		{UID: "pod-1", Container: "app", Data: []*util.Vector{{Timestamp: ts, Value: 0.1}}},
-	})
-	ds.Querier.SetOverride(source.QueryCPUUsageMax, []*source.CPUUsageMaxResult{
-		{UID: "pod-1", Container: "app", Data: []*util.Vector{{Timestamp: ts, Value: 0.2}}},
-	})
-	ds.Querier.SetOverride(source.QueryRAMBytesAllocated, []*source.RAMBytesAllocatedResult{
-		{UID: "pod-1", Container: "app", Data: []*util.Vector{{Timestamp: ts, Value: 256 * 1024 * 1024}}},
-	})
-	ds.Querier.SetOverride(source.QueryRAMUsageAvg, []*source.RAMUsageAvgResult{
-		{UID: "pod-1", Container: "app", Data: []*util.Vector{{Timestamp: ts, Value: 100 * 1024 * 1024}}},
-	})
-	ds.Querier.SetOverride(source.QueryRAMUsageMax, []*source.RAMUsageMaxResult{
-		{UID: "pod-1", Container: "app", Data: []*util.Vector{{Timestamp: ts, Value: 200 * 1024 * 1024}}},
-	})
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-
-	c := findContainer(kms, "pod-1", "app")
-	require.NotNil(t, c)
-	assert.Equal(t, 0.25, c.CPUCoresAllocated)
-	assert.Equal(t, 0.1, c.CPUCoreUsageAvg)
-	assert.Equal(t, 0.2, c.CPUCoreUsageMax)
-	assert.Equal(t, float64(256*1024*1024), c.RAMBytesAllocated)
-	assert.Equal(t, float64(100*1024*1024), c.RAMBytesUsageAvg)
-	assert.Equal(t, float64(200*1024*1024), c.RAMBytesUsageMax)
-}
-
-func TestComputeContainers_EmptyResults(t *testing.T) {
-	start, end := newTestWindow()
-	km, ds := newTestKubeModel(t)
-	seedCluster(ds, start, end)
-
-	kms, err := km.ComputeKubeModelSet(start, end)
-	require.NoError(t, err)
-	assert.Empty(t, kms.Containers)
-}
+	tests := []struct {
+		name    string
+		setup   func(ds *source.MockOpenCostDataSource)
+		wantErr bool
+		check   func(t *testing.T, kms *kubemodel.KubeModelSet)
+	}{
+		{
+			name: "cluster UID not found returns error",
+			setup: func(ds *source.MockOpenCostDataSource) {
+				ds.Querier.SetOverride(source.QueryClusterInfo, []*source.ClusterInfoResult{
+					{UID: "wrong-uid", Cluster: "other"},
+				})
+			},
+			wantErr: true,
+		},
+		{
+			name: "cluster uptime missing: cluster nil but other compute functions still run",
+			setup: func(ds *source.MockOpenCostDataSource) {
+				// cluster info present but no uptime — cluster won't register, but no error
+				ds.Querier.SetOverride(source.QueryClusterInfo, []*source.ClusterInfoResult{
+					{UID: testClusterUID, Cluster: "my-cluster"},
+				})
+				ds.Querier.SetOverride(source.QueryNamespaceInfo, []*source.NamespaceInfoResult{
+					{UID: "ns-1", Namespace: "default"},
+				})
+				ds.Querier.SetOverride(source.QueryNamespaceUptime, []*source.UptimeResult{
+					{UID: "ns-1", First: start, Last: end},
+				})
+			},
+			check: func(t *testing.T, kms *kubemodel.KubeModelSet) {
+				assert.Nil(t, kms.Cluster, "cluster with no uptime should not be registered")
+				assert.NotEmpty(t, kms.Namespaces, "namespace compute should still run after cluster registration fails")
+			},
+		},
+		{
+			name: "all compute functions produce results when given data",
+			setup: func(ds *source.MockOpenCostDataSource) {
+				seedCluster(ds, start, end)
+				ds.Querier.SetOverride(source.QueryNodeInfo, []*source.NodeInfoResult{
+					{UID: "node-1", Node: "node-a"},
+				})
+				ds.Querier.SetOverride(source.QueryNodeUptime, []*source.UptimeResult{
+					{UID: "node-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryNamespaceInfo, []*source.NamespaceInfoResult{
+					{UID: "ns-1", Namespace: "default"},
+				})
+				ds.Querier.SetOverride(source.QueryNamespaceUptime, []*source.UptimeResult{
+					{UID: "ns-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryPodInfo, []*source.PodInfoResult{
+					{UID: "pod-1", Pod: "my-pod", NamespaceUID: "ns-1"},
+				})
+				ds.Querier.SetOverride(source.QueryPodUptime, []*source.UptimeResult{
+					{UID: "pod-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryContainerUptime, []*source.ContainerUptimeResult{
+					{UptimeResult: source.UptimeResult{UID: "pod-1", First: start, Last: end}, Container: "app"},
+				})
+				ds.Querier.SetOverride(source.QueryDeploymentInfo, []*source.DeploymentInfoResult{
+					{UID: "dep-1", Deployment: "my-dep", NamespaceUID: "ns-1"},
+				})
+				ds.Querier.SetOverride(source.QueryDeploymentUptime, []*source.UptimeResult{
+					{UID: "dep-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryStatefulSetInfo, []*source.StatefulSetInfoResult{
+					{UID: "sts-1", StatefulSet: "my-sts", NamespaceUID: "ns-1"},
+				})
+				ds.Querier.SetOverride(source.QueryStatefulSetUptime, []*source.UptimeResult{
+					{UID: "sts-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryDaemonSetInfo, []*source.DaemonSetInfoResult{
+					{UID: "ds-1", DaemonSet: "my-ds", NamespaceUID: "ns-1"},
+				})
+				ds.Querier.SetOverride(source.QueryDaemonSetUptime, []*source.UptimeResult{
+					{UID: "ds-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryJobInfo, []*source.JobInfoResult{
+					{UID: "job-1", Job: "my-job", NamespaceUID: "ns-1"},
+				})
+				ds.Querier.SetOverride(source.QueryJobUptime, []*source.UptimeResult{
+					{UID: "job-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryCronJobInfo, []*source.CronJobInfoResult{
+					{UID: "cj-1", CronJob: "my-cj", NamespaceUID: "ns-1"},
+				})
+				ds.Querier.SetOverride(source.QueryCronJobUptime, []*source.UptimeResult{
+					{UID: "cj-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryReplicaSetInfo, []*source.ReplicaSetInfoResult{
+					{UID: "rs-1", ReplicaSet: "my-rs", NamespaceUID: "ns-1"},
+				})
+				ds.Querier.SetOverride(source.QueryReplicaSetUptime, []*source.UptimeResult{
+					{UID: "rs-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryResourceQuotaInfo, []*source.ResourceQuotaInfoResult{
+					{UID: "rq-1", ResourceQuota: "default-quota", NamespaceUID: "ns-1"},
+				})
+				ds.Querier.SetOverride(source.QueryResourceQuotaUptime, []*source.UptimeResult{
+					{UID: "rq-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryServiceInfo, []*source.ServiceInfoResult{
+					{UID: "svc-1", Service: "my-svc", NamespaceUID: "ns-1"},
+				})
+				ds.Querier.SetOverride(source.QueryServiceUptime, []*source.UptimeResult{
+					{UID: "svc-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryKMPVInfo, []*source.PVInfoResult{
+					{UID: "pv-1", PersistentVolume: "my-pv"},
+				})
+				ds.Querier.SetOverride(source.QueryPVUptime, []*source.UptimeResult{
+					{UID: "pv-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryPVBytes, []*source.PVBytesResult{
+					{UID: "pv-1", Value: 10 * 1024 * 1024 * 1024},
+				})
+				ds.Querier.SetOverride(source.QueryKMPVCInfo, []*source.PVCInfoResult{
+					{UID: "pvc-1", PersistentVolumeClaim: "data-claim", NamespaceUID: "ns-1"},
+				})
+				ds.Querier.SetOverride(source.QueryPVCUptime, []*source.UptimeResult{
+					{UID: "pvc-1", First: start, Last: end},
+				})
+				ds.Querier.SetOverride(source.QueryDCGMDeviceInfo, []*source.DCGMDeviceInfoResult{
+					{UUID: "GPU-abc123", Device: "nvidia0", ModelName: "A100"},
+				})
+				ds.Querier.SetOverride(source.QueryDCGMDeviceUptime, []*source.DCGMDeviceUptimeResult{
+					{UUID: "GPU-abc123", First: start, Last: end},
+				})
+			},
+			check: func(t *testing.T, kms *kubemodel.KubeModelSet) {
+				assert.NotNil(t, kms.Cluster)
+				assert.NotEmpty(t, kms.Nodes)
+				assert.NotEmpty(t, kms.Namespaces)
+				assert.NotEmpty(t, kms.Pods)
+				assert.NotEmpty(t, kms.Containers)
+				assert.NotEmpty(t, kms.Deployments)
+				assert.NotEmpty(t, kms.StatefulSets)
+				assert.NotEmpty(t, kms.DaemonSets)
+				assert.NotEmpty(t, kms.Jobs)
+				assert.NotEmpty(t, kms.CronJobs)
+				assert.NotEmpty(t, kms.ReplicaSets)
+				assert.NotEmpty(t, kms.ResourceQuotas)
+				assert.NotEmpty(t, kms.Services)
+				assert.NotEmpty(t, kms.PersistentVolumes)
+				assert.NotEmpty(t, kms.PersistentVolumeClaims)
+				assert.NotEmpty(t, kms.DCGMDevices)
+			},
+		},
+	}
 
-func findContainer(kms *kubemodel.KubeModelSet, podUID, name string) *kubemodel.Container {
-	for _, c := range kms.Containers {
-		if c.PodUID == podUID && c.Name == name {
-			return c
-		}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ds := source.NewMockOpenCostDataSource()
+			ds.ResolutionValue = 5 * time.Minute
+			tt.setup(ds)
+
+			km, err := NewKubeModel(testClusterUID, ds)
+			require.NoError(t, err)
+
+			kms, err := km.ComputeKubeModelSet(start, end)
+			if tt.wantErr {
+				require.Error(t, err)
+				return
+			}
+			require.NoError(t, err)
+			if tt.check != nil {
+				tt.check(t, kms)
+			}
+		})
 	}
-	return nil
 }