allocation_json_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package kubecost
  2. import (
  3. "encoding/json"
  4. "github.com/opencost/opencost/pkg/util/mathutil"
  5. "math"
  6. "testing"
  7. "time"
  8. )
  9. func TestAllocation_MarshalJSON(t *testing.T) {
  10. start := time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC)
  11. end := time.Date(2021, time.January, 2, 0, 0, 0, 0, time.UTC)
  12. hrs := 24.0
  13. gib := 1024.0 * 1024.0 * 1024.0
  14. cpuPrice := 0.02
  15. gpuPrice := 2.00
  16. ramPrice := 0.01
  17. pvPrice := 0.00005
  18. before := &Allocation{
  19. Name: "cluster1/namespace1/node1/pod1/container1",
  20. Properties: &AllocationProperties{
  21. Cluster: "cluster1",
  22. Node: "node1",
  23. Namespace: "namespace1",
  24. Pod: "pod1",
  25. Container: "container1",
  26. },
  27. Window: NewWindow(&start, &end),
  28. Start: start,
  29. End: end,
  30. CPUCoreHours: 2.0 * hrs,
  31. CPUCoreRequestAverage: 2.0,
  32. CPUCoreUsageAverage: 1.0,
  33. CPUCost: 2.0 * hrs * cpuPrice,
  34. CPUCostAdjustment: 3.0,
  35. GPUHours: 1.0 * hrs,
  36. GPUCost: 1.0 * hrs * gpuPrice,
  37. GPUCostAdjustment: 2.0,
  38. NetworkCost: 0.05,
  39. LoadBalancerCost: 0.02,
  40. PVs: PVAllocations{
  41. disk: {
  42. ByteHours: 100.0 * gib * hrs,
  43. Cost: 100.0 * hrs * pvPrice,
  44. },
  45. },
  46. PVCostAdjustment: 4.0,
  47. RAMByteHours: 8.0 * gib * hrs,
  48. RAMBytesRequestAverage: 8.0 * gib,
  49. RAMBytesUsageAverage: 4.0 * gib,
  50. RAMCost: 8.0 * hrs * ramPrice,
  51. RAMCostAdjustment: 1.0,
  52. SharedCost: 2.00,
  53. ExternalCost: 1.00,
  54. RawAllocationOnly: &RawAllocationOnlyData{},
  55. }
  56. data, err := json.Marshal(before)
  57. if err != nil {
  58. t.Fatalf("Allocation.MarshalJSON: unexpected error: %s", err)
  59. }
  60. after := &Allocation{}
  61. err = json.Unmarshal(data, after)
  62. if err != nil {
  63. t.Fatalf("Allocation.UnmarshalJSON: unexpected error: %s", err)
  64. }
  65. // TODO:CLEANUP fix json marshaling of Window so that all of this works.
  66. // In the meantime, just set the Window so that we can test the rest.
  67. after.Window = before.Window.Clone()
  68. if !after.Equal(before) {
  69. t.Fatalf("Allocation.MarshalJSON: before and after are not equal")
  70. }
  71. }
  72. func TestPVAllocations_MarshalJSON(t *testing.T) {
  73. testCases := map[string]PVAllocations{
  74. "empty": {},
  75. "single": {
  76. {
  77. Cluster: "cluster1",
  78. Name: "pv1",
  79. }: {
  80. ByteHours: 100,
  81. Cost: 1,
  82. },
  83. },
  84. "multi": {
  85. {
  86. Cluster: "cluster1",
  87. Name: "pv1",
  88. }: {
  89. ByteHours: 100,
  90. Cost: 1,
  91. },
  92. {
  93. Cluster: "cluster1",
  94. Name: "pv2",
  95. }: {
  96. ByteHours: 200,
  97. Cost: 2,
  98. },
  99. },
  100. "emptyPV": {
  101. {
  102. Cluster: "cluster1",
  103. Name: "pv1",
  104. }: {},
  105. },
  106. "emptyKey": {
  107. {}: {
  108. ByteHours: 100,
  109. Cost: 1,
  110. },
  111. },
  112. }
  113. for name, before := range testCases {
  114. t.Run(name, func(t *testing.T) {
  115. data, err := json.Marshal(before)
  116. if err != nil {
  117. t.Fatalf("PVAllocations.MarshalJSON: unexpected error: %s", err)
  118. }
  119. after := PVAllocations{}
  120. err = json.Unmarshal(data, &after)
  121. if err != nil {
  122. t.Fatalf("PVAllocations.UnmarshalJSON: unexpected error: %s", err)
  123. }
  124. if len(before) != len(after) {
  125. t.Fatalf("PVAllocations.MarshalJSON: before and after are not equal")
  126. }
  127. for pvKey, beforePV := range before {
  128. afterPV, ok := after[pvKey]
  129. if !ok {
  130. t.Fatalf("PVAllocations.MarshalJSON: after missing PVKey %s", pvKey)
  131. }
  132. if beforePV.Cost != afterPV.Cost {
  133. t.Fatalf("PVAllocations.MarshalJSON: PVAllocation Cost not equal for PVKey %s", pvKey)
  134. }
  135. if beforePV.ByteHours != afterPV.ByteHours {
  136. t.Fatalf("PVAllocations.MarshalJSON: PVAllocation ByteHours not equal for PVKey %s", pvKey)
  137. }
  138. }
  139. })
  140. }
  141. }
  142. func TestFormatFloat64ForResponse(t *testing.T) {
  143. type formatTestCase struct {
  144. name string
  145. input float64
  146. expectedNil bool
  147. expectedValue float64
  148. }
  149. testCases := []formatTestCase{
  150. {
  151. name: "zero",
  152. input: 0.0,
  153. expectedNil: false,
  154. expectedValue: 0.0,
  155. },
  156. {
  157. name: "round to zero",
  158. input: 0.000000001,
  159. expectedNil: false,
  160. expectedValue: 0,
  161. },
  162. {
  163. name: "valid value, no rounding",
  164. input: 14.123456,
  165. expectedNil: false,
  166. expectedValue: 14.123456,
  167. },
  168. {
  169. name: "valid value, with rounding",
  170. input: 14.1234567,
  171. expectedNil: false,
  172. expectedValue: 14.123457,
  173. },
  174. {
  175. name: "NaN is nil",
  176. input: math.NaN(),
  177. expectedNil: true,
  178. },
  179. {
  180. name: "infinite is nil",
  181. input: math.Inf(1),
  182. expectedNil: true,
  183. },
  184. {
  185. name: "negative infinite is nil",
  186. input: math.Inf(-1),
  187. expectedNil: true,
  188. },
  189. }
  190. for _, tc := range testCases {
  191. result := formatFloat64ForResponse(tc.input)
  192. if result == nil && tc.expectedNil == false {
  193. t.Fatalf("test case: %s: expected a value %f, got nil instead", tc.name, tc.expectedValue)
  194. }
  195. if result != nil && tc.expectedNil == true {
  196. t.Fatalf("test case: %s: expected nil, got value %f instead", tc.name, *result)
  197. }
  198. if result != nil && !mathutil.Approximately(*result, tc.expectedValue) {
  199. t.Fatalf("test case: %s: expected %f, got %f", tc.name, tc.expectedValue, *result)
  200. }
  201. }
  202. }