cloudcost_test.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. package kubecost
  2. import (
  3. "testing"
  4. "time"
  5. "github.com/opencost/opencost/pkg/util/timeutil"
  6. )
  7. var ccProperties1 = &CloudCostProperties{
  8. ProviderID: "providerid1",
  9. Provider: "provider1",
  10. AccountID: "workgroup1",
  11. InvoiceEntityID: "billing1",
  12. Service: "service1",
  13. Category: "category1",
  14. Labels: map[string]string{
  15. "label1": "value1",
  16. "label2": "value2",
  17. },
  18. }
  19. // TestCloudCost_LoadCloudCost checks that loaded CloudCosts end up in the correct set in the
  20. // correct proportions
  21. func TestCloudCost_LoadCloudCost(t *testing.T) {
  22. cc1Key := ccProperties1.GenerateKey(nil)
  23. // create values for 3 day Range tests
  24. end := RoundBack(time.Now().UTC(), timeutil.Day)
  25. start := end.Add(-3 * timeutil.Day)
  26. dayWindows, _ := GetWindows(start, end, timeutil.Day)
  27. emtpyCCSR, _ := NewCloudCostSetRange(start, end, timeutil.Day, "integration")
  28. testCases := map[string]struct {
  29. cc []*CloudCost
  30. ccsr *CloudCostSetRange
  31. expected []*CloudCostSet
  32. }{
  33. "Load Single Day On Grid": {
  34. cc: []*CloudCost{
  35. {
  36. Properties: ccProperties1,
  37. Window: dayWindows[0],
  38. ListCost: CostMetric{Cost: 100, KubernetesPercent: 1},
  39. NetCost: CostMetric{Cost: 80, KubernetesPercent: 1},
  40. AmortizedNetCost: CostMetric{Cost: 90, KubernetesPercent: 1},
  41. InvoicedCost: CostMetric{Cost: 95, KubernetesPercent: 1},
  42. },
  43. },
  44. ccsr: emtpyCCSR.Clone(),
  45. expected: []*CloudCostSet{
  46. {
  47. Integration: "integration",
  48. Window: dayWindows[0],
  49. CloudCosts: map[string]*CloudCost{
  50. cc1Key: {
  51. Properties: ccProperties1,
  52. Window: dayWindows[0],
  53. ListCost: CostMetric{Cost: 100, KubernetesPercent: 1},
  54. NetCost: CostMetric{Cost: 80, KubernetesPercent: 1},
  55. AmortizedNetCost: CostMetric{Cost: 90, KubernetesPercent: 1},
  56. InvoicedCost: CostMetric{Cost: 95, KubernetesPercent: 1},
  57. },
  58. },
  59. },
  60. {
  61. Integration: "integration",
  62. Window: dayWindows[1],
  63. CloudCosts: map[string]*CloudCost{},
  64. },
  65. {
  66. Integration: "integration",
  67. Window: dayWindows[2],
  68. CloudCosts: map[string]*CloudCost{},
  69. },
  70. },
  71. },
  72. "Load Single Day Off Grid": {
  73. cc: []*CloudCost{
  74. {
  75. Properties: ccProperties1,
  76. Window: NewClosedWindow(start.Add(12*time.Hour), start.Add(36*time.Hour)),
  77. ListCost: CostMetric{Cost: 100, KubernetesPercent: 1},
  78. NetCost: CostMetric{Cost: 80, KubernetesPercent: 1},
  79. AmortizedNetCost: CostMetric{Cost: 90, KubernetesPercent: 1},
  80. InvoicedCost: CostMetric{Cost: 95, KubernetesPercent: 1},
  81. },
  82. },
  83. ccsr: emtpyCCSR.Clone(),
  84. expected: []*CloudCostSet{
  85. {
  86. Integration: "integration",
  87. Window: dayWindows[0],
  88. CloudCosts: map[string]*CloudCost{
  89. cc1Key: {
  90. Properties: ccProperties1,
  91. Window: NewClosedWindow(start.Add(12*time.Hour), start.Add(24*time.Hour)),
  92. ListCost: CostMetric{Cost: 50, KubernetesPercent: 1},
  93. NetCost: CostMetric{Cost: 40, KubernetesPercent: 1},
  94. AmortizedNetCost: CostMetric{Cost: 45, KubernetesPercent: 1},
  95. InvoicedCost: CostMetric{Cost: 47.5, KubernetesPercent: 1},
  96. },
  97. },
  98. },
  99. {
  100. Integration: "integration",
  101. Window: dayWindows[1],
  102. CloudCosts: map[string]*CloudCost{
  103. cc1Key: {
  104. Properties: ccProperties1,
  105. Window: NewClosedWindow(start.Add(24*time.Hour), start.Add(36*time.Hour)),
  106. ListCost: CostMetric{Cost: 50, KubernetesPercent: 1},
  107. NetCost: CostMetric{Cost: 40, KubernetesPercent: 1},
  108. AmortizedNetCost: CostMetric{Cost: 45, KubernetesPercent: 1},
  109. InvoicedCost: CostMetric{Cost: 47.5, KubernetesPercent: 1},
  110. },
  111. },
  112. },
  113. {
  114. Integration: "integration",
  115. Window: dayWindows[2],
  116. CloudCosts: map[string]*CloudCost{},
  117. },
  118. },
  119. },
  120. "Load Single Day Off Grid Before Range Window": {
  121. cc: []*CloudCost{
  122. {
  123. Properties: ccProperties1,
  124. Window: NewClosedWindow(start.Add(-12*time.Hour), start.Add(12*time.Hour)),
  125. ListCost: CostMetric{Cost: 100, KubernetesPercent: 1},
  126. NetCost: CostMetric{Cost: 80, KubernetesPercent: 1},
  127. AmortizedNetCost: CostMetric{Cost: 90, KubernetesPercent: 1},
  128. InvoicedCost: CostMetric{Cost: 95, KubernetesPercent: 1},
  129. },
  130. },
  131. ccsr: emtpyCCSR.Clone(),
  132. expected: []*CloudCostSet{
  133. {
  134. Integration: "integration",
  135. Window: dayWindows[0],
  136. CloudCosts: map[string]*CloudCost{
  137. cc1Key: {
  138. Properties: ccProperties1,
  139. Window: NewClosedWindow(start, start.Add(12*time.Hour)),
  140. ListCost: CostMetric{Cost: 50, KubernetesPercent: 1},
  141. NetCost: CostMetric{Cost: 40, KubernetesPercent: 1},
  142. AmortizedNetCost: CostMetric{Cost: 45, KubernetesPercent: 1},
  143. InvoicedCost: CostMetric{Cost: 47.5, KubernetesPercent: 1},
  144. },
  145. },
  146. },
  147. {
  148. Integration: "integration",
  149. Window: dayWindows[1],
  150. CloudCosts: map[string]*CloudCost{},
  151. },
  152. {
  153. Integration: "integration",
  154. Window: dayWindows[2],
  155. CloudCosts: map[string]*CloudCost{},
  156. },
  157. },
  158. },
  159. "Load Single Day Off Grid After Range Window": {
  160. cc: []*CloudCost{
  161. {
  162. Properties: ccProperties1,
  163. Window: NewClosedWindow(end.Add(-12*time.Hour), end.Add(12*time.Hour)),
  164. ListCost: CostMetric{Cost: 100, KubernetesPercent: 1},
  165. NetCost: CostMetric{Cost: 80, KubernetesPercent: 1},
  166. AmortizedNetCost: CostMetric{Cost: 90, KubernetesPercent: 1},
  167. InvoicedCost: CostMetric{Cost: 95, KubernetesPercent: 1},
  168. },
  169. },
  170. ccsr: emtpyCCSR.Clone(),
  171. expected: []*CloudCostSet{
  172. {
  173. Integration: "integration",
  174. Window: dayWindows[0],
  175. CloudCosts: map[string]*CloudCost{},
  176. },
  177. {
  178. Integration: "integration",
  179. Window: dayWindows[1],
  180. CloudCosts: map[string]*CloudCost{},
  181. },
  182. {
  183. Integration: "integration",
  184. Window: dayWindows[2],
  185. CloudCosts: map[string]*CloudCost{
  186. cc1Key: {
  187. Properties: ccProperties1,
  188. Window: NewClosedWindow(end.Add(-12*time.Hour), end),
  189. ListCost: CostMetric{Cost: 50, KubernetesPercent: 1},
  190. NetCost: CostMetric{Cost: 40, KubernetesPercent: 1},
  191. AmortizedNetCost: CostMetric{Cost: 45, KubernetesPercent: 1},
  192. InvoicedCost: CostMetric{Cost: 47.5, KubernetesPercent: 1},
  193. },
  194. },
  195. },
  196. },
  197. },
  198. "Single Day Kubernetes Percent": {
  199. cc: []*CloudCost{
  200. {
  201. Properties: ccProperties1,
  202. Window: dayWindows[1],
  203. ListCost: CostMetric{Cost: 75, KubernetesPercent: 1},
  204. NetCost: CostMetric{Cost: 40, KubernetesPercent: 1},
  205. AmortizedNetCost: CostMetric{Cost: 60, KubernetesPercent: 1},
  206. InvoicedCost: CostMetric{Cost: 50, KubernetesPercent: 1},
  207. },
  208. {
  209. Properties: ccProperties1,
  210. Window: dayWindows[1],
  211. ListCost: CostMetric{Cost: 25, KubernetesPercent: 0},
  212. NetCost: CostMetric{Cost: 60, KubernetesPercent: 0},
  213. AmortizedNetCost: CostMetric{Cost: 40, KubernetesPercent: 0},
  214. InvoicedCost: CostMetric{Cost: 50, KubernetesPercent: 0},
  215. },
  216. },
  217. ccsr: emtpyCCSR.Clone(),
  218. expected: []*CloudCostSet{
  219. {
  220. Integration: "integration",
  221. Window: dayWindows[0],
  222. CloudCosts: map[string]*CloudCost{},
  223. },
  224. {
  225. Integration: "integration",
  226. Window: dayWindows[1],
  227. CloudCosts: map[string]*CloudCost{
  228. cc1Key: {
  229. Properties: ccProperties1,
  230. Window: dayWindows[1],
  231. ListCost: CostMetric{Cost: 100, KubernetesPercent: 0.75},
  232. NetCost: CostMetric{Cost: 100, KubernetesPercent: 0.4},
  233. AmortizedNetCost: CostMetric{Cost: 100, KubernetesPercent: 0.6},
  234. InvoicedCost: CostMetric{Cost: 100, KubernetesPercent: 0.5},
  235. },
  236. },
  237. },
  238. {
  239. Integration: "integration",
  240. Window: dayWindows[2],
  241. CloudCosts: map[string]*CloudCost{},
  242. },
  243. },
  244. },
  245. }
  246. for name, tc := range testCases {
  247. t.Run(name, func(t *testing.T) {
  248. // load Cloud Costs
  249. for _, cc := range tc.cc {
  250. tc.ccsr.LoadCloudCost(cc)
  251. }
  252. if len(tc.ccsr.CloudCostSets) != len(tc.expected) {
  253. t.Errorf("the CloudCostSetRanges did not have the expected length")
  254. }
  255. for i, ccs := range tc.ccsr.CloudCostSets {
  256. if !ccs.Equal(tc.expected[i]) {
  257. t.Errorf("CloudCostSet at index: %d did not match expected", i)
  258. }
  259. }
  260. })
  261. }
  262. }