cloudcostaggregate_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. package kubecost
  2. import (
  3. "testing"
  4. "time"
  5. "github.com/opencost/opencost/pkg/util/timeutil"
  6. )
  7. var ccaProperties1 = CloudCostAggregateProperties{
  8. Provider: "provider1",
  9. WorkGroupID: "workgroup1",
  10. BillingID: "billing1",
  11. Service: "service1",
  12. LabelValue: "labelValue1",
  13. }
  14. func TestCloudCostAggregatePropertiesIntersection(t *testing.T) {
  15. testCases := map[string]struct {
  16. baseCCAP CloudCostAggregateProperties
  17. intCCAP CloudCostAggregateProperties
  18. expectedCCAP CloudCostAggregateProperties
  19. }{
  20. "When properties match between both CloudCostAggregateProperties": {
  21. baseCCAP: CloudCostAggregateProperties{
  22. Provider: "CustomProvider",
  23. WorkGroupID: "WorkGroupID1",
  24. BillingID: "BillingID1",
  25. Service: "Service1",
  26. LabelValue: "Label1",
  27. },
  28. intCCAP: CloudCostAggregateProperties{
  29. Provider: "CustomProvider",
  30. WorkGroupID: "WorkGroupID1",
  31. BillingID: "BillingID1",
  32. Service: "Service1",
  33. LabelValue: "Label1",
  34. },
  35. expectedCCAP: CloudCostAggregateProperties{
  36. Provider: "CustomProvider",
  37. WorkGroupID: "WorkGroupID1",
  38. BillingID: "BillingID1",
  39. Service: "Service1",
  40. LabelValue: "Label1",
  41. },
  42. },
  43. "When one of the properties differ in the two CloudCostAggregateProperties": {
  44. baseCCAP: CloudCostAggregateProperties{
  45. Provider: "CustomProvider",
  46. WorkGroupID: "WorkGroupID1",
  47. BillingID: "BillingID1",
  48. Service: "Service1",
  49. LabelValue: "Label1",
  50. },
  51. intCCAP: CloudCostAggregateProperties{
  52. Provider: "CustomProvider",
  53. WorkGroupID: "WorkGroupID1",
  54. BillingID: "BillingID1",
  55. Service: "Service2",
  56. LabelValue: "Label1",
  57. },
  58. expectedCCAP: CloudCostAggregateProperties{
  59. Provider: "CustomProvider",
  60. WorkGroupID: "WorkGroupID1",
  61. BillingID: "BillingID1",
  62. Service: "",
  63. LabelValue: "Label1",
  64. },
  65. },
  66. "When two of the properties differ in the two CloudCostAggregateProperties": {
  67. baseCCAP: CloudCostAggregateProperties{
  68. Provider: "CustomProvider",
  69. WorkGroupID: "WorkGroupID1",
  70. BillingID: "BillingID1",
  71. Service: "Service1",
  72. LabelValue: "Label1",
  73. },
  74. intCCAP: CloudCostAggregateProperties{
  75. Provider: "CustomProvider",
  76. WorkGroupID: "WorkGroupID2",
  77. BillingID: "BillingID1",
  78. Service: "Service2",
  79. LabelValue: "Label1",
  80. },
  81. expectedCCAP: CloudCostAggregateProperties{
  82. Provider: "CustomProvider",
  83. WorkGroupID: "",
  84. BillingID: "BillingID1",
  85. Service: "",
  86. LabelValue: "Label1",
  87. },
  88. },
  89. }
  90. for name, tc := range testCases {
  91. t.Run(name, func(t *testing.T) {
  92. actualCCAP := tc.baseCCAP.Intersection(tc.intCCAP)
  93. if actualCCAP.Provider != tc.expectedCCAP.Provider {
  94. t.Errorf("Case %s: Provider properties dont match with expected CloudCostAggregateProperties: %v actual %v", name, tc.expectedCCAP, actualCCAP)
  95. }
  96. if actualCCAP.WorkGroupID != tc.expectedCCAP.WorkGroupID {
  97. t.Errorf("Case %s: WorkGroupID properties dont match with expected CloudCostAggregateProperties: %v actual %v", name, tc.expectedCCAP, actualCCAP)
  98. }
  99. if actualCCAP.BillingID != tc.expectedCCAP.BillingID {
  100. t.Errorf("Case %s: BillingID properties dont match with expected CloudCostAggregateProperties: %v actual %v", name, tc.expectedCCAP, actualCCAP)
  101. }
  102. if actualCCAP.Service != tc.expectedCCAP.Service {
  103. t.Errorf("Case %s: Service properties dont match with expected CloudCostAggregateProperties: %v actual %v", name, tc.expectedCCAP, actualCCAP)
  104. }
  105. if actualCCAP.LabelValue != tc.expectedCCAP.LabelValue {
  106. t.Errorf("Case %s: LabelValue properties dont match with expected CloudCostAggregateProperties: %v actual %v", name, tc.expectedCCAP, actualCCAP)
  107. }
  108. })
  109. }
  110. }
  111. // TestCloudCostAggregate_LoadCloudCostAggregate checks that loaded CloudCostAggregates end up in the correct set in the
  112. // correct proportions
  113. func TestCloudCostAggregate_LoadCloudCostAggregate(t *testing.T) {
  114. // create values for 3 day Range tests
  115. end := RoundBack(time.Now().UTC(), timeutil.Day)
  116. start := end.Add(-3 * timeutil.Day)
  117. dayWindows, _ := GetWindows(start, end, timeutil.Day)
  118. emtpyCASSR, _ := NewCloudCostAggregateSetRange(start, end, timeutil.Day, "integration", "label")
  119. testCases := map[string]struct {
  120. cca []*CloudCostAggregate
  121. windows []Window
  122. ccasr *CloudCostAggregateSetRange
  123. expected []*CloudCostAggregateSet
  124. }{
  125. "Load Single Day On Grid": {
  126. cca: []*CloudCostAggregate{
  127. {
  128. Properties: ccaProperties1,
  129. KubernetesPercent: 1,
  130. Cost: 100,
  131. NetCost: 80,
  132. },
  133. },
  134. windows: []Window{
  135. dayWindows[0],
  136. },
  137. ccasr: emtpyCASSR.Clone(),
  138. expected: []*CloudCostAggregateSet{
  139. {
  140. Integration: "integration",
  141. LabelName: "label",
  142. Window: dayWindows[0],
  143. CloudCostAggregates: map[string]*CloudCostAggregate{
  144. ccaProperties1.Key(nil): {
  145. Properties: ccaProperties1,
  146. KubernetesPercent: 1,
  147. Cost: 100,
  148. NetCost: 80,
  149. },
  150. },
  151. },
  152. {
  153. Integration: "integration",
  154. LabelName: "label",
  155. Window: dayWindows[1],
  156. CloudCostAggregates: map[string]*CloudCostAggregate{},
  157. },
  158. {
  159. Integration: "integration",
  160. LabelName: "label",
  161. Window: dayWindows[2],
  162. CloudCostAggregates: map[string]*CloudCostAggregate{},
  163. },
  164. },
  165. },
  166. "Load Single Day Off Grid": {
  167. cca: []*CloudCostAggregate{
  168. {
  169. Properties: ccaProperties1,
  170. KubernetesPercent: 1,
  171. Cost: 100,
  172. NetCost: 80,
  173. },
  174. },
  175. windows: []Window{
  176. NewClosedWindow(start.Add(12*time.Hour), start.Add(36*time.Hour)),
  177. },
  178. ccasr: emtpyCASSR.Clone(),
  179. expected: []*CloudCostAggregateSet{
  180. {
  181. Integration: "integration",
  182. LabelName: "label",
  183. Window: dayWindows[0],
  184. CloudCostAggregates: map[string]*CloudCostAggregate{
  185. ccaProperties1.Key(nil): {
  186. Properties: ccaProperties1,
  187. KubernetesPercent: 1,
  188. Cost: 50,
  189. NetCost: 40,
  190. },
  191. },
  192. },
  193. {
  194. Integration: "integration",
  195. LabelName: "label",
  196. Window: dayWindows[1],
  197. CloudCostAggregates: map[string]*CloudCostAggregate{
  198. ccaProperties1.Key(nil): {
  199. Properties: ccaProperties1,
  200. KubernetesPercent: 1,
  201. Cost: 50,
  202. NetCost: 40,
  203. },
  204. },
  205. },
  206. {
  207. Integration: "integration",
  208. LabelName: "label",
  209. Window: dayWindows[2],
  210. CloudCostAggregates: map[string]*CloudCostAggregate{},
  211. },
  212. },
  213. },
  214. "Load Single Day Off Grid Before Range Window": {
  215. cca: []*CloudCostAggregate{
  216. {
  217. Properties: ccaProperties1,
  218. KubernetesPercent: 1,
  219. Cost: 100,
  220. NetCost: 80,
  221. },
  222. },
  223. windows: []Window{
  224. NewClosedWindow(start.Add(-12*time.Hour), start.Add(12*time.Hour)),
  225. },
  226. ccasr: emtpyCASSR.Clone(),
  227. expected: []*CloudCostAggregateSet{
  228. {
  229. Integration: "integration",
  230. LabelName: "label",
  231. Window: dayWindows[0],
  232. CloudCostAggregates: map[string]*CloudCostAggregate{
  233. ccaProperties1.Key(nil): {
  234. Properties: ccaProperties1,
  235. KubernetesPercent: 1,
  236. Cost: 50,
  237. NetCost: 40,
  238. },
  239. },
  240. },
  241. {
  242. Integration: "integration",
  243. LabelName: "label",
  244. Window: dayWindows[1],
  245. CloudCostAggregates: map[string]*CloudCostAggregate{},
  246. },
  247. {
  248. Integration: "integration",
  249. LabelName: "label",
  250. Window: dayWindows[2],
  251. CloudCostAggregates: map[string]*CloudCostAggregate{},
  252. },
  253. },
  254. },
  255. "Load Single Day Off Grid After Range Window": {
  256. cca: []*CloudCostAggregate{
  257. {
  258. Properties: ccaProperties1,
  259. KubernetesPercent: 1,
  260. Cost: 100,
  261. NetCost: 80,
  262. },
  263. },
  264. windows: []Window{
  265. NewClosedWindow(end.Add(-12*time.Hour), end.Add(12*time.Hour)),
  266. },
  267. ccasr: emtpyCASSR.Clone(),
  268. expected: []*CloudCostAggregateSet{
  269. {
  270. Integration: "integration",
  271. LabelName: "label",
  272. Window: dayWindows[0],
  273. CloudCostAggregates: map[string]*CloudCostAggregate{},
  274. },
  275. {
  276. Integration: "integration",
  277. LabelName: "label",
  278. Window: dayWindows[1],
  279. CloudCostAggregates: map[string]*CloudCostAggregate{},
  280. },
  281. {
  282. Integration: "integration",
  283. LabelName: "label",
  284. Window: dayWindows[2],
  285. CloudCostAggregates: map[string]*CloudCostAggregate{
  286. ccaProperties1.Key(nil): {
  287. Properties: ccaProperties1,
  288. KubernetesPercent: 1,
  289. Cost: 50,
  290. NetCost: 40,
  291. },
  292. },
  293. },
  294. },
  295. },
  296. "Single Day Kubecost Percent": {
  297. cca: []*CloudCostAggregate{
  298. {
  299. Properties: ccaProperties1,
  300. KubernetesPercent: 1,
  301. Cost: 75,
  302. NetCost: 60,
  303. },
  304. {
  305. Properties: ccaProperties1,
  306. KubernetesPercent: 0,
  307. Cost: 25,
  308. NetCost: 20,
  309. },
  310. },
  311. windows: []Window{
  312. dayWindows[1],
  313. dayWindows[1],
  314. },
  315. ccasr: emtpyCASSR.Clone(),
  316. expected: []*CloudCostAggregateSet{
  317. {
  318. Integration: "integration",
  319. LabelName: "label",
  320. Window: dayWindows[0],
  321. CloudCostAggregates: map[string]*CloudCostAggregate{},
  322. },
  323. {
  324. Integration: "integration",
  325. LabelName: "label",
  326. Window: dayWindows[1],
  327. CloudCostAggregates: map[string]*CloudCostAggregate{
  328. ccaProperties1.Key(nil): {
  329. Properties: ccaProperties1,
  330. KubernetesPercent: 0.75,
  331. Cost: 100,
  332. NetCost: 80,
  333. },
  334. },
  335. },
  336. {
  337. Integration: "integration",
  338. LabelName: "label",
  339. Window: dayWindows[2],
  340. CloudCostAggregates: map[string]*CloudCostAggregate{},
  341. },
  342. },
  343. },
  344. }
  345. for name, tc := range testCases {
  346. t.Run(name, func(t *testing.T) {
  347. // load Cloud Cost Aggregates
  348. for i, cca := range tc.cca {
  349. tc.ccasr.LoadCloudCostAggregate(tc.windows[i], cca)
  350. }
  351. if len(tc.ccasr.CloudCostAggregateSets) != len(tc.expected) {
  352. t.Errorf("the CloudCostAggregateSetRanges did not have the expected length")
  353. }
  354. for i, ccas := range tc.ccasr.CloudCostAggregateSets {
  355. if !ccas.Equal(tc.expected[i]) {
  356. t.Errorf("CloudCostAggregateSet at index: %d did not match expected", i)
  357. }
  358. }
  359. })
  360. }
  361. }