totals_providerid_test.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. package opencost
  2. import (
  3. "testing"
  4. "time"
  5. )
  6. // TestComputeAssetTotals_ProviderID_ByNode verifies that when computing
  7. // AssetTotals by node (byAsset=true), the ProviderID field is correctly
  8. // populated from the node's ProviderID.
  9. func TestComputeAssetTotals_ProviderID_ByNode(t *testing.T) {
  10. // Create test window
  11. start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
  12. end := time.Date(2024, 1, 1, 1, 0, 0, 0, time.UTC)
  13. window := NewClosedWindow(start, end)
  14. // Create AssetSet with nodes that have ProviderIDs
  15. as := NewAssetSet(start, end)
  16. // Node 1: AWS EC2 instance
  17. node1 := NewNode("node1", "cluster1", "aws:///us-east-1a/i-0abc123def456789", start, end, window)
  18. node1.CPUCost = 10.0
  19. node1.RAMCost = 5.0
  20. // Node 2: GCP instance
  21. node2 := NewNode("node2", "cluster1", "gce://my-project/us-central1-a/instance-456", start, end, window)
  22. node2.CPUCost = 8.0
  23. node2.RAMCost = 4.0
  24. // Node 3: Azure VM
  25. node3 := NewNode("node3", "cluster1", "azure:///subscriptions/sub-id/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm-789", start, end, window)
  26. node3.CPUCost = 12.0
  27. node3.RAMCost = 6.0
  28. as.Insert(node1, nil)
  29. as.Insert(node2, nil)
  30. as.Insert(node3, nil)
  31. // Compute AssetTotals by node (byAsset=true)
  32. totals := ComputeAssetTotals(as, true)
  33. // Verify that each node's totals includes the correct ProviderID
  34. expectedProviderIDs := map[string]string{
  35. "cluster1/node1": "aws:///us-east-1a/i-0abc123def456789",
  36. "cluster1/node2": "gce://my-project/us-central1-a/instance-456",
  37. "cluster1/node3": "azure:///subscriptions/sub-id/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm-789",
  38. }
  39. for key, expectedProviderID := range expectedProviderIDs {
  40. total, ok := totals[key]
  41. if !ok {
  42. t.Errorf("Expected to find totals for key %s", key)
  43. continue
  44. }
  45. if total.ProviderID != expectedProviderID {
  46. t.Errorf("For key %s: expected ProviderID %q, got %q", key, expectedProviderID, total.ProviderID)
  47. }
  48. // Verify Node field is also set correctly
  49. expectedNodeName := key[len("cluster1/"):]
  50. if total.Node != expectedNodeName {
  51. t.Errorf("For key %s: expected Node %q, got %q", key, expectedNodeName, total.Node)
  52. }
  53. }
  54. }
  55. // TestComputeAssetTotals_ProviderID_ByCluster verifies that when computing
  56. // AssetTotals by cluster (byAsset=false), the ProviderID field is empty
  57. // because there's no single instance ID that represents all nodes.
  58. func TestComputeAssetTotals_ProviderID_ByCluster(t *testing.T) {
  59. // Create test window
  60. start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
  61. end := time.Date(2024, 1, 1, 1, 0, 0, 0, time.UTC)
  62. window := NewClosedWindow(start, end)
  63. // Create AssetSet with nodes that have ProviderIDs
  64. as := NewAssetSet(start, end)
  65. node1 := NewNode("node1", "cluster1", "aws:///us-east-1a/i-0abc123def456789", start, end, window)
  66. node1.CPUCost = 10.0
  67. node1.RAMCost = 5.0
  68. node2 := NewNode("node2", "cluster1", "gce://my-project/us-central1-a/instance-456", start, end, window)
  69. node2.CPUCost = 8.0
  70. node2.RAMCost = 4.0
  71. as.Insert(node1, nil)
  72. as.Insert(node2, nil)
  73. // Compute AssetTotals by cluster (byAsset=false)
  74. totals := ComputeAssetTotals(as, false)
  75. // Verify that cluster-level totals have an empty ProviderID
  76. total, ok := totals["cluster1"]
  77. if !ok {
  78. t.Fatal("Expected to find totals for cluster1")
  79. }
  80. if total.ProviderID != "" {
  81. t.Errorf("Expected empty ProviderID for cluster-level totals, got %q", total.ProviderID)
  82. }
  83. // Verify Node field is also empty at cluster level
  84. if total.Node != "" {
  85. t.Errorf("Expected empty Node for cluster-level totals, got %q", total.Node)
  86. }
  87. // Verify costs are aggregated correctly
  88. expectedCPUCost := 18.0 // 10.0 + 8.0
  89. expectedRAMCost := 9.0 // 5.0 + 4.0
  90. if total.CPUCost != expectedCPUCost {
  91. t.Errorf("Expected CPUCost %f, got %f", expectedCPUCost, total.CPUCost)
  92. }
  93. if total.RAMCost != expectedRAMCost {
  94. t.Errorf("Expected RAMCost %f, got %f", expectedRAMCost, total.RAMCost)
  95. }
  96. }
  97. // TestComputeAssetTotals_ProviderID_EmptyProviderID verifies that nodes
  98. // without a ProviderID still work correctly and result in an empty string
  99. // in the AssetTotals.
  100. func TestComputeAssetTotals_ProviderID_EmptyProviderID(t *testing.T) {
  101. // Create test window
  102. start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
  103. end := time.Date(2024, 1, 1, 1, 0, 0, 0, time.UTC)
  104. window := NewClosedWindow(start, end)
  105. // Create AssetSet with a node that has no ProviderID
  106. as := NewAssetSet(start, end)
  107. // Node without ProviderID (e.g., local/bare-metal cluster)
  108. node := NewNode("node1", "cluster1", "", start, end, window)
  109. node.CPUCost = 10.0
  110. node.RAMCost = 5.0
  111. as.Insert(node, nil)
  112. // Compute AssetTotals by node (byAsset=true)
  113. totals := ComputeAssetTotals(as, true)
  114. // Verify that the node's totals has an empty ProviderID
  115. total, ok := totals["cluster1/node1"]
  116. if !ok {
  117. t.Fatal("Expected to find totals for cluster1/node1")
  118. }
  119. if total.ProviderID != "" {
  120. t.Errorf("Expected empty ProviderID for node without ProviderID, got %q", total.ProviderID)
  121. }
  122. // Verify other fields are still populated correctly
  123. if total.Node != "node1" {
  124. t.Errorf("Expected Node %q, got %q", "node1", total.Node)
  125. }
  126. if total.CPUCost != 10.0 {
  127. t.Errorf("Expected CPUCost %f, got %f", 10.0, total.CPUCost)
  128. }
  129. }
  130. // TestComputeAssetTotals_ProviderID_MultipleNodesAggregation tests that
  131. // when multiple nodes with different ProviderIDs are aggregated at the
  132. // cluster level, the ProviderID remains empty.
  133. func TestComputeAssetTotals_ProviderID_MultipleNodesAggregation(t *testing.T) {
  134. // Create test window
  135. start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
  136. end := time.Date(2024, 1, 1, 1, 0, 0, 0, time.UTC)
  137. window := NewClosedWindow(start, end)
  138. // Create AssetSet with multiple nodes across different clusters
  139. as := NewAssetSet(start, end)
  140. // Cluster 1 nodes
  141. node1 := NewNode("node1", "cluster1", "aws:///us-east-1a/i-0abc123", start, end, window)
  142. node1.CPUCost = 10.0
  143. node1.RAMCost = 5.0
  144. node2 := NewNode("node2", "cluster1", "aws:///us-east-1b/i-0def456", start, end, window)
  145. node2.CPUCost = 8.0
  146. node2.RAMCost = 4.0
  147. // Cluster 2 node
  148. node3 := NewNode("node3", "cluster2", "gce://project/zone/instance", start, end, window)
  149. node3.CPUCost = 12.0
  150. node3.RAMCost = 6.0
  151. as.Insert(node1, nil)
  152. as.Insert(node2, nil)
  153. as.Insert(node3, nil)
  154. // Compute AssetTotals by node (byAsset=true)
  155. nodeTotal := ComputeAssetTotals(as, true)
  156. // Verify each node has its own ProviderID
  157. if nodeTotal["cluster1/node1"].ProviderID != "aws:///us-east-1a/i-0abc123" {
  158. t.Errorf("Node1 ProviderID mismatch")
  159. }
  160. if nodeTotal["cluster1/node2"].ProviderID != "aws:///us-east-1b/i-0def456" {
  161. t.Errorf("Node2 ProviderID mismatch")
  162. }
  163. if nodeTotal["cluster2/node3"].ProviderID != "gce://project/zone/instance" {
  164. t.Errorf("Node3 ProviderID mismatch")
  165. }
  166. // Compute AssetTotals by cluster (byAsset=false)
  167. clusterTotals := ComputeAssetTotals(as, false)
  168. // Verify cluster-level totals have empty ProviderID
  169. for clusterKey, total := range clusterTotals {
  170. if total.ProviderID != "" {
  171. t.Errorf("Cluster %s should have empty ProviderID, got %q", clusterKey, total.ProviderID)
  172. }
  173. }
  174. // Verify cluster1 aggregates both nodes correctly
  175. cluster1Total := clusterTotals["cluster1"]
  176. expectedCPU := 18.0 // 10.0 + 8.0
  177. expectedRAM := 9.0 // 5.0 + 4.0
  178. if cluster1Total.CPUCost != expectedCPU {
  179. t.Errorf("Cluster1 CPUCost: expected %f, got %f", expectedCPU, cluster1Total.CPUCost)
  180. }
  181. if cluster1Total.RAMCost != expectedRAM {
  182. t.Errorf("Cluster1 RAMCost: expected %f, got %f", expectedRAM, cluster1Total.RAMCost)
  183. }
  184. }