cloudcost_test.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. package opencost
  2. import (
  3. "testing"
  4. "time"
  5. "github.com/opencost/opencost/core/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, AccumulateOptionDay, "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. AmortizedCost: CostMetric{Cost: 85, KubernetesPercent: 1},
  43. },
  44. },
  45. ccsr: emtpyCCSR.Clone(),
  46. expected: []*CloudCostSet{
  47. {
  48. Integration: "integration",
  49. Window: dayWindows[0],
  50. CloudCosts: map[string]*CloudCost{
  51. cc1Key: {
  52. Properties: ccProperties1,
  53. Window: dayWindows[0],
  54. ListCost: CostMetric{Cost: 100, KubernetesPercent: 1},
  55. NetCost: CostMetric{Cost: 80, KubernetesPercent: 1},
  56. AmortizedNetCost: CostMetric{Cost: 90, KubernetesPercent: 1},
  57. InvoicedCost: CostMetric{Cost: 95, KubernetesPercent: 1},
  58. AmortizedCost: CostMetric{Cost: 85, KubernetesPercent: 1},
  59. },
  60. },
  61. },
  62. {
  63. Integration: "integration",
  64. Window: dayWindows[1],
  65. CloudCosts: map[string]*CloudCost{},
  66. },
  67. {
  68. Integration: "integration",
  69. Window: dayWindows[2],
  70. CloudCosts: map[string]*CloudCost{},
  71. },
  72. },
  73. },
  74. "Load Single Day Off Grid": {
  75. cc: []*CloudCost{
  76. {
  77. Properties: ccProperties1,
  78. Window: NewClosedWindow(start.Add(12*time.Hour), start.Add(36*time.Hour)),
  79. ListCost: CostMetric{Cost: 100, KubernetesPercent: 1},
  80. NetCost: CostMetric{Cost: 80, KubernetesPercent: 1},
  81. AmortizedNetCost: CostMetric{Cost: 90, KubernetesPercent: 1},
  82. InvoicedCost: CostMetric{Cost: 95, KubernetesPercent: 1},
  83. AmortizedCost: CostMetric{Cost: 85, KubernetesPercent: 1},
  84. },
  85. },
  86. ccsr: emtpyCCSR.Clone(),
  87. expected: []*CloudCostSet{
  88. {
  89. Integration: "integration",
  90. Window: dayWindows[0],
  91. CloudCosts: map[string]*CloudCost{
  92. cc1Key: {
  93. Properties: ccProperties1,
  94. Window: NewClosedWindow(start.Add(12*time.Hour), start.Add(24*time.Hour)),
  95. ListCost: CostMetric{Cost: 50, KubernetesPercent: 1},
  96. NetCost: CostMetric{Cost: 40, KubernetesPercent: 1},
  97. AmortizedNetCost: CostMetric{Cost: 45, KubernetesPercent: 1},
  98. InvoicedCost: CostMetric{Cost: 47.5, KubernetesPercent: 1},
  99. AmortizedCost: CostMetric{Cost: 42.5, KubernetesPercent: 1},
  100. },
  101. },
  102. },
  103. {
  104. Integration: "integration",
  105. Window: dayWindows[1],
  106. CloudCosts: map[string]*CloudCost{
  107. cc1Key: {
  108. Properties: ccProperties1,
  109. Window: NewClosedWindow(start.Add(24*time.Hour), start.Add(36*time.Hour)),
  110. ListCost: CostMetric{Cost: 50, KubernetesPercent: 1},
  111. NetCost: CostMetric{Cost: 40, KubernetesPercent: 1},
  112. AmortizedNetCost: CostMetric{Cost: 45, KubernetesPercent: 1},
  113. InvoicedCost: CostMetric{Cost: 47.5, KubernetesPercent: 1},
  114. AmortizedCost: CostMetric{Cost: 42.5, KubernetesPercent: 1},
  115. },
  116. },
  117. },
  118. {
  119. Integration: "integration",
  120. Window: dayWindows[2],
  121. CloudCosts: map[string]*CloudCost{},
  122. },
  123. },
  124. },
  125. "Load Single Day Off Grid Before Range Window": {
  126. cc: []*CloudCost{
  127. {
  128. Properties: ccProperties1,
  129. Window: NewClosedWindow(start.Add(-12*time.Hour), start.Add(12*time.Hour)),
  130. ListCost: CostMetric{Cost: 100, KubernetesPercent: 1},
  131. NetCost: CostMetric{Cost: 80, KubernetesPercent: 1},
  132. AmortizedNetCost: CostMetric{Cost: 90, KubernetesPercent: 1},
  133. InvoicedCost: CostMetric{Cost: 95, KubernetesPercent: 1},
  134. AmortizedCost: CostMetric{Cost: 85, KubernetesPercent: 1},
  135. },
  136. },
  137. ccsr: emtpyCCSR.Clone(),
  138. expected: []*CloudCostSet{
  139. {
  140. Integration: "integration",
  141. Window: dayWindows[0],
  142. CloudCosts: map[string]*CloudCost{
  143. cc1Key: {
  144. Properties: ccProperties1,
  145. Window: NewClosedWindow(start, start.Add(12*time.Hour)),
  146. ListCost: CostMetric{Cost: 50, KubernetesPercent: 1},
  147. NetCost: CostMetric{Cost: 40, KubernetesPercent: 1},
  148. AmortizedNetCost: CostMetric{Cost: 45, KubernetesPercent: 1},
  149. InvoicedCost: CostMetric{Cost: 47.5, KubernetesPercent: 1},
  150. AmortizedCost: CostMetric{Cost: 42.5, KubernetesPercent: 1},
  151. },
  152. },
  153. },
  154. {
  155. Integration: "integration",
  156. Window: dayWindows[1],
  157. CloudCosts: map[string]*CloudCost{},
  158. },
  159. {
  160. Integration: "integration",
  161. Window: dayWindows[2],
  162. CloudCosts: map[string]*CloudCost{},
  163. },
  164. },
  165. },
  166. "Load Single Day Off Grid After Range Window": {
  167. cc: []*CloudCost{
  168. {
  169. Properties: ccProperties1,
  170. Window: NewClosedWindow(end.Add(-12*time.Hour), end.Add(12*time.Hour)),
  171. ListCost: CostMetric{Cost: 100, KubernetesPercent: 1},
  172. NetCost: CostMetric{Cost: 80, KubernetesPercent: 1},
  173. AmortizedNetCost: CostMetric{Cost: 90, KubernetesPercent: 1},
  174. InvoicedCost: CostMetric{Cost: 95, KubernetesPercent: 1},
  175. AmortizedCost: CostMetric{Cost: 85, KubernetesPercent: 1},
  176. },
  177. },
  178. ccsr: emtpyCCSR.Clone(),
  179. expected: []*CloudCostSet{
  180. {
  181. Integration: "integration",
  182. Window: dayWindows[0],
  183. CloudCosts: map[string]*CloudCost{},
  184. },
  185. {
  186. Integration: "integration",
  187. Window: dayWindows[1],
  188. CloudCosts: map[string]*CloudCost{},
  189. },
  190. {
  191. Integration: "integration",
  192. Window: dayWindows[2],
  193. CloudCosts: map[string]*CloudCost{
  194. cc1Key: {
  195. Properties: ccProperties1,
  196. Window: NewClosedWindow(end.Add(-12*time.Hour), end),
  197. ListCost: CostMetric{Cost: 50, KubernetesPercent: 1},
  198. NetCost: CostMetric{Cost: 40, KubernetesPercent: 1},
  199. AmortizedNetCost: CostMetric{Cost: 45, KubernetesPercent: 1},
  200. InvoicedCost: CostMetric{Cost: 47.5, KubernetesPercent: 1},
  201. AmortizedCost: CostMetric{Cost: 42.5, KubernetesPercent: 1},
  202. },
  203. },
  204. },
  205. },
  206. },
  207. "Single Day Kubernetes Percent": {
  208. cc: []*CloudCost{
  209. {
  210. Properties: ccProperties1,
  211. Window: dayWindows[1],
  212. ListCost: CostMetric{Cost: 75, KubernetesPercent: 1},
  213. NetCost: CostMetric{Cost: 40, KubernetesPercent: 1},
  214. AmortizedNetCost: CostMetric{Cost: 60, KubernetesPercent: 1},
  215. InvoicedCost: CostMetric{Cost: 50, KubernetesPercent: 1},
  216. AmortizedCost: CostMetric{Cost: 80, KubernetesPercent: 1},
  217. },
  218. {
  219. Properties: ccProperties1,
  220. Window: dayWindows[1],
  221. ListCost: CostMetric{Cost: 25, KubernetesPercent: 0},
  222. NetCost: CostMetric{Cost: 60, KubernetesPercent: 0},
  223. AmortizedNetCost: CostMetric{Cost: 40, KubernetesPercent: 0},
  224. InvoicedCost: CostMetric{Cost: 50, KubernetesPercent: 0},
  225. AmortizedCost: CostMetric{Cost: 20, KubernetesPercent: 0},
  226. },
  227. },
  228. ccsr: emtpyCCSR.Clone(),
  229. expected: []*CloudCostSet{
  230. {
  231. Integration: "integration",
  232. Window: dayWindows[0],
  233. CloudCosts: map[string]*CloudCost{},
  234. },
  235. {
  236. Integration: "integration",
  237. Window: dayWindows[1],
  238. CloudCosts: map[string]*CloudCost{
  239. cc1Key: {
  240. Properties: ccProperties1,
  241. Window: dayWindows[1],
  242. ListCost: CostMetric{Cost: 100, KubernetesPercent: 0.75},
  243. NetCost: CostMetric{Cost: 100, KubernetesPercent: 0.4},
  244. AmortizedNetCost: CostMetric{Cost: 100, KubernetesPercent: 0.6},
  245. InvoicedCost: CostMetric{Cost: 100, KubernetesPercent: 0.5},
  246. AmortizedCost: CostMetric{Cost: 100, KubernetesPercent: 0.8},
  247. },
  248. },
  249. },
  250. {
  251. Integration: "integration",
  252. Window: dayWindows[2],
  253. CloudCosts: map[string]*CloudCost{},
  254. },
  255. },
  256. },
  257. }
  258. for name, tc := range testCases {
  259. t.Run(name, func(t *testing.T) {
  260. // load Cloud Costs
  261. for _, cc := range tc.cc {
  262. tc.ccsr.LoadCloudCost(cc)
  263. }
  264. if len(tc.ccsr.CloudCostSets) != len(tc.expected) {
  265. t.Errorf("the CloudCostSetRanges did not have the expected length")
  266. }
  267. for i, ccs := range tc.ccsr.CloudCostSets {
  268. if !ccs.Equal(tc.expected[i]) {
  269. t.Errorf("CloudCostSet at index: %d did not match expected", i)
  270. }
  271. }
  272. })
  273. }
  274. }