asset_test.go 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813
  1. package opencost
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "math"
  6. "reflect"
  7. "testing"
  8. "time"
  9. "github.com/opencost/opencost/core/pkg/util"
  10. )
  11. var start1 = time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC)
  12. var start2 = start1.Add(day)
  13. var start3 = start2.Add(day)
  14. var start4 = start2.Add(day)
  15. var windows = []Window{
  16. NewWindow(&start1, &start2),
  17. NewWindow(&start2, &start3),
  18. NewWindow(&start3, &start4),
  19. }
  20. func assertAssetSet(t *testing.T, as *AssetSet, msg string, window Window, exps map[string]float64, err error) {
  21. if err != nil {
  22. t.Fatalf("AssetSet.AggregateBy[%s]: unexpected error: %s", msg, err)
  23. }
  24. if as.Length() != len(exps) {
  25. t.Fatalf("AssetSet.AggregateBy[%s]: expected set of length %d, actual %d", msg, len(exps), as.Length())
  26. }
  27. if !as.Window.Equal(window) {
  28. t.Fatalf("AssetSet.AggregateBy[%s]: expected window %s, actual %s", msg, window, as.Window)
  29. }
  30. for key, a := range as.Assets {
  31. if exp, ok := exps[key]; ok {
  32. if math.Round(a.TotalCost()*100) != math.Round(exp*100) {
  33. t.Fatalf("AssetSet.AggregateBy[%s]: key %s expected total cost %.2f, actual %.2f", msg, key, exp, a.TotalCost())
  34. }
  35. if !a.GetWindow().Equal(window) {
  36. t.Fatalf("AssetSet.AggregateBy[%s]: key %s expected window %s, actual %s", msg, key, window, a.GetWindow())
  37. }
  38. } else {
  39. t.Fatalf("AssetSet.AggregateBy[%s]: unexpected asset: %s", msg, key)
  40. }
  41. }
  42. }
  43. func printAssetSet(msg string, as *AssetSet) {
  44. fmt.Printf("--- %s ---\n", msg)
  45. for key, a := range as.Assets {
  46. fmt.Printf(" > %s: %s\n", key, a)
  47. }
  48. }
  49. func TestAny_Add(t *testing.T) {
  50. any1 := NewAsset(*windows[0].start, *windows[0].end, windows[0])
  51. any1.SetProperties(&AssetProperties{
  52. Name: "any1",
  53. Cluster: "cluster1",
  54. ProviderID: "any1",
  55. })
  56. any1.Cost = 9.0
  57. any1.SetAdjustment(1.0)
  58. any2 := NewAsset(*windows[0].start, *windows[0].end, windows[0])
  59. any2.SetProperties(&AssetProperties{
  60. Name: "any2",
  61. Cluster: "cluster1",
  62. ProviderID: "any2",
  63. })
  64. any2.Cost = 4.0
  65. any2.SetAdjustment(1.0)
  66. any3 := any1.Add(any2)
  67. // Check that the sums and properties are correct
  68. if any3.TotalCost() != 15.0 {
  69. t.Fatalf("Any.Add: expected %f; got %f", 15.0, any3.TotalCost())
  70. }
  71. if any3.GetAdjustment() != 2.0 {
  72. t.Fatalf("Any.Add: expected %f; got %f", 2.0, any3.GetAdjustment())
  73. }
  74. if any3.GetProperties().Cluster != "cluster1" {
  75. t.Fatalf("Any.Add: expected %s; got %s", "cluster1", any3.GetProperties().Cluster)
  76. }
  77. if any3.Type() != AnyAssetType {
  78. t.Fatalf("Any.Add: expected %s; got %s", AnyAssetType, any3.Type())
  79. }
  80. if any3.GetProperties().ProviderID != "" {
  81. t.Fatalf("Any.Add: expected %s; got %s", "", any3.GetProperties().ProviderID)
  82. }
  83. if any3.GetProperties().Name != "" {
  84. t.Fatalf("Any.Add: expected %s; got %s", "", any3.GetProperties().Name)
  85. }
  86. // Check that the original assets are unchanged
  87. if any1.TotalCost() != 10.0 {
  88. t.Fatalf("Any.Add: expected %f; got %f", 10.0, any1.TotalCost())
  89. }
  90. if any1.Adjustment != 1.0 {
  91. t.Fatalf("Any.Add: expected %f; got %f", 1.0, any1.Adjustment)
  92. }
  93. if any2.TotalCost() != 5.0 {
  94. t.Fatalf("Any.Add: expected %f; got %f", 5.0, any2.TotalCost())
  95. }
  96. if any2.Adjustment != 1.0 {
  97. t.Fatalf("Any.Add: expected %f; got %f", 1.0, any2.Adjustment)
  98. }
  99. }
  100. func TestAny_Clone(t *testing.T) {
  101. any1 := NewAsset(*windows[0].start, *windows[0].end, windows[0])
  102. any1.SetProperties(&AssetProperties{
  103. Name: "any1",
  104. Cluster: "cluster1",
  105. ProviderID: "any1",
  106. })
  107. any1.Cost = 9.0
  108. any1.SetAdjustment(1.0)
  109. any2 := any1.Clone()
  110. any1.Cost = 18.0
  111. any1.SetAdjustment(2.0)
  112. // any2 should match any1, even after mutating any1
  113. if any2.TotalCost() != 10.0 {
  114. t.Fatalf("Any.Clone: expected %f; got %f", 10.0, any2.TotalCost())
  115. }
  116. if any2.GetAdjustment() != 1.0 {
  117. t.Fatalf("Any.Clone: expected %f; got %f", 1.0, any2.GetAdjustment())
  118. }
  119. }
  120. func TestAny_MarshalJSON(t *testing.T) {
  121. any1 := NewAsset(*windows[0].start, *windows[0].end, windows[0])
  122. any1.SetProperties(&AssetProperties{
  123. Name: "any1",
  124. Cluster: "cluster1",
  125. ProviderID: "any1",
  126. })
  127. any1.Cost = 9.0
  128. any1.SetAdjustment(1.0)
  129. _, err := json.Marshal(any1)
  130. if err != nil {
  131. t.Fatalf("Any.MarshalJSON: unexpected error: %s", err)
  132. }
  133. any2 := NewAsset(*windows[0].start, *windows[0].end, windows[0])
  134. any2.SetProperties(&AssetProperties{
  135. Name: "any2",
  136. Cluster: "cluster1",
  137. ProviderID: "any2",
  138. })
  139. any2.Cost = math.NaN()
  140. any2.SetAdjustment(1.0)
  141. _, err = json.Marshal(any2)
  142. if err != nil {
  143. t.Fatalf("Any.MarshalJSON: unexpected error: %s", err)
  144. }
  145. }
  146. func TestDisk_Add(t *testing.T) {
  147. // 1. aggregate: add size, local
  148. // 2. accumulate: don't add size, local
  149. hours := windows[0].Duration().Hours()
  150. // Aggregate: two disks, one window
  151. disk1 := NewDisk("disk1", "cluster1", "disk1", *windows[0].start, *windows[0].end, windows[0])
  152. disk1.ByteHours = 100.0 * gb * hours
  153. disk1.Cost = 9.0
  154. disk1.SetAdjustment(1.0)
  155. if disk1.Bytes() != 100.0*gb {
  156. t.Fatalf("Disk.Add: expected %f; got %f", 100.0*gb, disk1.Bytes())
  157. }
  158. disk2 := NewDisk("disk2", "cluster1", "disk2", *windows[0].start, *windows[0].end, windows[0])
  159. disk2.ByteHours = 60.0 * gb * hours
  160. disk2.Cost = 4.0
  161. disk2.Local = 1.0
  162. disk2.SetAdjustment(1.0)
  163. if disk2.Bytes() != 60.0*gb {
  164. t.Fatalf("Disk.Add: expected %f; got %f", 60.0*gb, disk2.Bytes())
  165. }
  166. diskT := disk1.Add(disk2).(*Disk)
  167. // Check that the sums and properties are correct
  168. if diskT.TotalCost() != 15.0 {
  169. t.Fatalf("Disk.Add: expected %f; got %f", 15.0, diskT.TotalCost())
  170. }
  171. if diskT.Adjustment != 2.0 {
  172. t.Fatalf("Disk.Add: expected %f; got %f", 2.0, diskT.Adjustment)
  173. }
  174. if diskT.Properties.Cluster != "cluster1" {
  175. t.Fatalf("Disk.Add: expected %s; got %s", "cluster1", diskT.Properties.Cluster)
  176. }
  177. if diskT.Type() != DiskAssetType {
  178. t.Fatalf("Disk.Add: expected %s; got %s", AnyAssetType, diskT.Type())
  179. }
  180. if diskT.Properties.ProviderID != "" {
  181. t.Fatalf("Disk.Add: expected %s; got %s", "", diskT.Properties.ProviderID)
  182. }
  183. if diskT.Properties.Name != "" {
  184. t.Fatalf("Disk.Add: expected %s; got %s", "", diskT.Properties.Name)
  185. }
  186. if diskT.Bytes() != 160.0*gb {
  187. t.Fatalf("Disk.Add: expected %f; got %f", 160.0*gb, diskT.Bytes())
  188. }
  189. if !util.IsApproximately(diskT.Local, 0.333333) {
  190. t.Fatalf("Disk.Add: expected %f; got %f", 0.333333, diskT.Local)
  191. }
  192. // Check that the original assets are unchanged
  193. if disk1.TotalCost() != 10.0 {
  194. t.Fatalf("Disk.Add: expected %f; got %f", 10.0, disk1.TotalCost())
  195. }
  196. if disk1.Adjustment != 1.0 {
  197. t.Fatalf("Disk.Add: expected %f; got %f", 1.0, disk1.Adjustment)
  198. }
  199. if disk1.Local != 0.0 {
  200. t.Fatalf("Disk.Add: expected %f; got %f", 0.0, disk1.Local)
  201. }
  202. if disk2.TotalCost() != 5.0 {
  203. t.Fatalf("Disk.Add: expected %f; got %f", 5.0, disk2.TotalCost())
  204. }
  205. if disk2.Adjustment != 1.0 {
  206. t.Fatalf("Disk.Add: expected %f; got %f", 1.0, disk2.Adjustment)
  207. }
  208. if disk2.Local != 1.0 {
  209. t.Fatalf("Disk.Add: expected %f; got %f", 1.0, disk2.Local)
  210. }
  211. disk3 := NewDisk("disk3", "cluster1", "disk3", *windows[0].start, *windows[0].end, windows[0])
  212. disk3.ByteHours = 0.0 * hours
  213. disk3.Cost = 0.0
  214. disk3.Local = 0.0
  215. disk3.SetAdjustment(0.0)
  216. disk4 := NewDisk("disk4", "cluster1", "disk4", *windows[0].start, *windows[0].end, windows[0])
  217. disk4.ByteHours = 0.0 * hours
  218. disk4.Cost = 0.0
  219. disk4.Local = 1.0
  220. disk4.SetAdjustment(0.0)
  221. diskT = disk3.Add(disk4).(*Disk)
  222. if diskT.TotalCost() != 0.0 {
  223. t.Fatalf("Disk.Add: expected %f; got %f", 0.0, diskT.TotalCost())
  224. }
  225. if diskT.Local != 0.5 {
  226. t.Fatalf("Disk.Add: expected %f; got %f", 0.5, diskT.Local)
  227. }
  228. // Accumulate: one disks, two windows
  229. diskA1 := NewDisk("diskA1", "cluster1", "diskA1", *windows[0].start, *windows[0].end, windows[0])
  230. diskA1.ByteHours = 100 * gb * hours
  231. diskA1.Cost = 9.0
  232. diskA1.SetAdjustment(1.0)
  233. diskA2 := NewDisk("diskA2", "cluster1", "diskA2", *windows[1].start, *windows[1].end, windows[1])
  234. diskA2.ByteHours = 100 * gb * hours
  235. diskA2.Cost = 9.0
  236. diskA2.SetAdjustment(1.0)
  237. diskAT := diskA1.Add(diskA2).(*Disk)
  238. // Check that the sums and properties are correct
  239. if diskAT.TotalCost() != 20.0 {
  240. t.Fatalf("Disk.Add: expected %f; got %f", 20.0, diskAT.TotalCost())
  241. }
  242. if diskAT.Adjustment != 2.0 {
  243. t.Fatalf("Disk.Add: expected %f; got %f", 2.0, diskAT.Adjustment)
  244. }
  245. if diskAT.Properties.Cluster != "cluster1" {
  246. t.Fatalf("Disk.Add: expected %s; got %s", "cluster1", diskAT.Properties.Cluster)
  247. }
  248. if diskAT.Type() != DiskAssetType {
  249. t.Fatalf("Disk.Add: expected %s; got %s", AnyAssetType, diskAT.Type())
  250. }
  251. if diskAT.Properties.ProviderID != "" {
  252. t.Fatalf("Disk.Add: expected %s; got %s", "", diskAT.Properties.ProviderID)
  253. }
  254. if diskAT.Properties.Name != "" {
  255. t.Fatalf("Disk.Add: expected %s; got %s", "", diskAT.Properties.Name)
  256. }
  257. if diskAT.Bytes() != 100.0*gb {
  258. t.Fatalf("Disk.Add: expected %f; got %f", 100.0*gb, diskT.Bytes())
  259. }
  260. if diskAT.Local != 0.0 {
  261. t.Fatalf("Disk.Add: expected %f; got %f", 0.0, diskAT.Local)
  262. }
  263. // Check that the original assets are unchanged
  264. if diskA1.TotalCost() != 10.0 {
  265. t.Fatalf("Disk.Add: expected %f; got %f", 10.0, diskA1.TotalCost())
  266. }
  267. if diskA1.Adjustment != 1.0 {
  268. t.Fatalf("Disk.Add: expected %f; got %f", 1.0, diskA1.Adjustment)
  269. }
  270. if diskA1.Local != 0.0 {
  271. t.Fatalf("Disk.Add: expected %f; got %f", 0.0, diskA1.Local)
  272. }
  273. if diskA2.TotalCost() != 10.0 {
  274. t.Fatalf("Disk.Add: expected %f; got %f", 10.0, diskA2.TotalCost())
  275. }
  276. if diskA2.Adjustment != 1.0 {
  277. t.Fatalf("Disk.Add: expected %f; got %f", 1.0, diskA2.Adjustment)
  278. }
  279. if diskA2.Local != 0.0 {
  280. t.Fatalf("Disk.Add: expected %f; got %f", 0.0, diskA2.Local)
  281. }
  282. }
  283. func TestDisk_Clone(t *testing.T) {
  284. disk1 := NewDisk("disk1", "cluster1", "disk1", *windows[0].start, *windows[0].end, windows[0])
  285. disk1.Local = 0.0
  286. disk1.Cost = 9.0
  287. disk1.SetAdjustment(1.0)
  288. disk2 := disk1.Clone().(*Disk)
  289. disk2.Local = 1.0
  290. disk1.Cost = 18.0
  291. disk1.SetAdjustment(2.0)
  292. // disk2 should match disk1, even after mutating disk1
  293. if disk2.TotalCost() != 10.0 {
  294. t.Fatalf("Any.Clone: expected %f; got %f", 10.0, disk2.TotalCost())
  295. }
  296. if disk2.Adjustment != 1.0 {
  297. t.Fatalf("Any.Clone: expected %f; got %f", 1.0, disk2.Adjustment)
  298. }
  299. if disk2.Local != 1.0 {
  300. t.Fatalf("Disk.Add: expected %f; got %f", 1.0, disk2.Local)
  301. }
  302. }
  303. func TestDisk_MarshalJSON(t *testing.T) {
  304. disk := NewDisk("disk", "cluster", "providerID", *windows[0].start, *windows[0].end, windows[0])
  305. disk.SetLabels(AssetLabels{
  306. "label": "value",
  307. })
  308. disk.Cost = 9.0
  309. disk.SetAdjustment(1.0)
  310. _, err := json.Marshal(disk)
  311. if err != nil {
  312. t.Fatalf("Disk.MarshalJSON: unexpected error: %s", err)
  313. }
  314. }
  315. func TestNode_Add(t *testing.T) {
  316. // 1. aggregate: add size, local
  317. // 2. accumulate: don't add size, local
  318. hours := windows[0].Duration().Hours()
  319. // Aggregate: two nodes, one window
  320. node1 := NewNode("node1", "cluster1", "node1", *windows[0].start, *windows[0].end, windows[0])
  321. node1.CPUCoreHours = 1.0 * hours
  322. node1.RAMByteHours = 2.0 * gb * hours
  323. node1.GPUHours = 0.0 * hours
  324. node1.GPUCost = 0.0
  325. node1.CPUCost = 8.0
  326. node1.RAMCost = 4.0
  327. node1.Discount = 0.3
  328. node1.CPUBreakdown = &Breakdown{
  329. Idle: 0.6,
  330. System: 0.2,
  331. User: 0.2,
  332. Other: 0.0,
  333. }
  334. node1.RAMBreakdown = &Breakdown{
  335. Idle: 0.6,
  336. System: 0.2,
  337. User: 0.2,
  338. Other: 0.0,
  339. }
  340. node1.SetAdjustment(1.6)
  341. node1.Overhead = &NodeOverhead{
  342. CpuOverheadFraction: 1,
  343. RamOverheadFraction: 1,
  344. OverheadCostFraction: 1,
  345. }
  346. node2 := NewNode("node2", "cluster1", "node2", *windows[0].start, *windows[0].end, windows[0])
  347. node2.CPUCoreHours = 1.0 * hours
  348. node2.RAMByteHours = 2.0 * gb * hours
  349. node2.GPUHours = 0.0 * hours
  350. node2.GPUCost = 0.0
  351. node2.CPUCost = 3.0
  352. node2.RAMCost = 1.0
  353. node2.Discount = 0.0
  354. node1.CPUBreakdown = &Breakdown{
  355. Idle: 0.9,
  356. System: 0.05,
  357. User: 0.0,
  358. Other: 0.05,
  359. }
  360. node1.RAMBreakdown = &Breakdown{
  361. Idle: 0.9,
  362. System: 0.05,
  363. User: 0.0,
  364. Other: 0.05,
  365. }
  366. node2.SetAdjustment(1.0)
  367. node2.Overhead = &NodeOverhead{
  368. CpuOverheadFraction: 0.6,
  369. RamOverheadFraction: 0.75,
  370. OverheadCostFraction: 0.7,
  371. }
  372. nodeT := node1.Add(node2).(*Node)
  373. // Check that the sums and properties are correct
  374. if !util.IsApproximately(nodeT.TotalCost(), 15.0) {
  375. t.Fatalf("Node.Add: expected %f; got %f", 15.0, nodeT.TotalCost())
  376. }
  377. if nodeT.Adjustment != 2.6 {
  378. t.Fatalf("Node.Add: expected %f; got %f", 2.6, nodeT.Adjustment)
  379. }
  380. if nodeT.Properties.Cluster != "cluster1" {
  381. t.Fatalf("Node.Add: expected %s; got %s", "cluster1", nodeT.Properties.Cluster)
  382. }
  383. if nodeT.Type() != NodeAssetType {
  384. t.Fatalf("Node.Add: expected %s; got %s", AnyAssetType, nodeT.Type())
  385. }
  386. if nodeT.Properties.ProviderID != "" {
  387. t.Fatalf("Node.Add: expected %s; got %s", "", nodeT.Properties.ProviderID)
  388. }
  389. if nodeT.Properties.Name != "" {
  390. t.Fatalf("Node.Add: expected %s; got %s", "", nodeT.Properties.Name)
  391. }
  392. if nodeT.CPUCores() != 2.0 {
  393. t.Fatalf("Node.Add: expected %f; got %f", 2.0, nodeT.CPUCores())
  394. }
  395. if nodeT.RAMBytes() != 4.0*gb {
  396. t.Fatalf("Node.Add: expected %f; got %f", 4.0*gb, nodeT.RAMBytes())
  397. }
  398. if o := nodeT.Overhead; o == nil {
  399. t.Errorf("Node.Add (1 + 2): expected overhead to be non-nil")
  400. } else {
  401. if o.CpuOverheadFraction < 0 || o.CpuOverheadFraction > 1 {
  402. t.Errorf("CPU overhead must be within [0, 1], is: %f", o.CpuOverheadFraction)
  403. }
  404. if o.RamOverheadFraction < 0 || o.RamOverheadFraction > 1 {
  405. t.Errorf("RAM overhead must be within [0, 1], is: %f", o.RamOverheadFraction)
  406. }
  407. if o.OverheadCostFraction < 0 || o.OverheadCostFraction > 1 {
  408. t.Errorf("Cost-weighted overhead must be within [0, 1], is: %f", o.OverheadCostFraction)
  409. }
  410. }
  411. // Check that the original assets are unchanged
  412. if !util.IsApproximately(node1.TotalCost(), 10.0) {
  413. t.Fatalf("Node.Add: expected %f; got %f", 10.0, node1.TotalCost())
  414. }
  415. if node1.Adjustment != 1.6 {
  416. t.Fatalf("Node.Add: expected %f; got %f", 1.0, node1.Adjustment)
  417. }
  418. if !util.IsApproximately(node2.TotalCost(), 5.0) {
  419. t.Fatalf("Node.Add: expected %f; got %f", 5.0, node2.TotalCost())
  420. }
  421. if node2.Adjustment != 1.0 {
  422. t.Fatalf("Node.Add: expected %f; got %f", 1.0, node2.Adjustment)
  423. }
  424. // Check that we don't divide by zero computing Local
  425. node3 := NewNode("node3", "cluster1", "node3", *windows[0].start, *windows[0].end, windows[0])
  426. node3.CPUCoreHours = 0 * hours
  427. node3.RAMByteHours = 0 * hours
  428. node3.GPUHours = 0.0 * hours
  429. node3.GPUCost = 0
  430. node3.CPUCost = 0.0
  431. node3.RAMCost = 0.0
  432. node3.Discount = 0.3
  433. node3.SetAdjustment(0.0)
  434. node3.Overhead = &NodeOverhead{
  435. CpuOverheadFraction: 0.6,
  436. RamOverheadFraction: 0.75,
  437. OverheadCostFraction: 0.7,
  438. }
  439. node4 := NewNode("node4", "cluster1", "node4", *windows[0].start, *windows[0].end, windows[0])
  440. node4.CPUCoreHours = 0 * hours
  441. node4.RAMByteHours = 0 * hours
  442. node4.GPUHours = 0.0 * hours
  443. node4.GPUCost = 0
  444. node4.CPUCost = 0.0
  445. node4.RAMCost = 0.0
  446. node4.Discount = 0.1
  447. node4.SetAdjustment(0.0)
  448. node4.Overhead = nil
  449. nodeT = node3.Add(node4).(*Node)
  450. // Check that the sums and properties are correct and without NaNs
  451. if nodeT.TotalCost() != 0.0 {
  452. t.Fatalf("Node.Add: expected %f; got %f", 0.0, nodeT.TotalCost())
  453. }
  454. if nodeT.Discount != 0.2 {
  455. t.Fatalf("Node.Add: expected %f; got %f", 0.2, nodeT.Discount)
  456. }
  457. if nodeT.Overhead != nil {
  458. t.Errorf("Node.Add: adding a node with nil overhead should nil the resulting overhead")
  459. }
  460. // Accumulate: one nodes, two window
  461. nodeA1 := NewNode("nodeA1", "cluster1", "nodeA1", *windows[0].start, *windows[0].end, windows[0])
  462. nodeA1.CPUCoreHours = 1.0 * hours
  463. nodeA1.RAMByteHours = 2.0 * gb * hours
  464. nodeA1.GPUHours = 0.0 * hours
  465. nodeA1.GPUCost = 0.0
  466. nodeA1.CPUCost = 8.0
  467. nodeA1.RAMCost = 4.0
  468. nodeA1.Discount = 0.3
  469. nodeA1.SetAdjustment(1.6)
  470. nodeA2 := NewNode("nodeA2", "cluster1", "nodeA2", *windows[1].start, *windows[1].end, windows[1])
  471. nodeA2.CPUCoreHours = 1.0 * hours
  472. nodeA2.RAMByteHours = 2.0 * gb * hours
  473. nodeA2.GPUHours = 0.0 * hours
  474. nodeA2.GPUCost = 0.0
  475. nodeA2.CPUCost = 3.0
  476. nodeA2.RAMCost = 1.0
  477. nodeA2.Discount = 0.0
  478. nodeA2.SetAdjustment(1.0)
  479. nodeAT := nodeA1.Add(nodeA2).(*Node)
  480. // Check that the sums and properties are correct
  481. if !util.IsApproximately(nodeAT.TotalCost(), 15.0) {
  482. t.Fatalf("Node.Add: expected %f; got %f", 15.0, nodeAT.TotalCost())
  483. }
  484. if nodeAT.Adjustment != 2.6 {
  485. t.Fatalf("Node.Add: expected %f; got %f", 2.6, nodeAT.Adjustment)
  486. }
  487. if nodeAT.Properties.Cluster != "cluster1" {
  488. t.Fatalf("Node.Add: expected %s; got %s", "cluster1", nodeAT.Properties.Cluster)
  489. }
  490. if nodeAT.Type() != NodeAssetType {
  491. t.Fatalf("Node.Add: expected %s; got %s", AnyAssetType, nodeAT.Type())
  492. }
  493. if nodeAT.Properties.ProviderID != "" {
  494. t.Fatalf("Node.Add: expected %s; got %s", "", nodeAT.Properties.ProviderID)
  495. }
  496. if nodeAT.Properties.Name != "" {
  497. t.Fatalf("Node.Add: expected %s; got %s", "", nodeAT.Properties.Name)
  498. }
  499. if nodeAT.CPUCores() != 1.0 {
  500. t.Fatalf("Node.Add: expected %f; got %f", 1.0, nodeAT.CPUCores())
  501. }
  502. if nodeAT.RAMBytes() != 2.0*gb {
  503. t.Fatalf("Node.Add: expected %f; got %f", 2.0*gb, nodeAT.RAMBytes())
  504. }
  505. if nodeAT.GPUs() != 0.0 {
  506. t.Fatalf("Node.Add: expected %f; got %f", 0.0, nodeAT.GPUs())
  507. }
  508. // Check that the original assets are unchanged
  509. if !util.IsApproximately(nodeA1.TotalCost(), 10.0) {
  510. t.Fatalf("Node.Add: expected %f; got %f", 10.0, nodeA1.TotalCost())
  511. }
  512. if nodeA1.Adjustment != 1.6 {
  513. t.Fatalf("Node.Add: expected %f; got %f", 1.0, nodeA1.Adjustment)
  514. }
  515. if !util.IsApproximately(nodeA2.TotalCost(), 5.0) {
  516. t.Fatalf("Node.Add: expected %f; got %f", 5.0, nodeA2.TotalCost())
  517. }
  518. if nodeA2.Adjustment != 1.0 {
  519. t.Fatalf("Node.Add: expected %f; got %f", 1.0, nodeA2.Adjustment)
  520. }
  521. }
  522. func TestNode_Clone(t *testing.T) {
  523. cases := []struct {
  524. name string
  525. input *Node
  526. }{
  527. {
  528. name: "overhead nil",
  529. input: &Node{
  530. Overhead: nil,
  531. },
  532. },
  533. {
  534. name: "overhead non-nil",
  535. input: &Node{
  536. Overhead: &NodeOverhead{
  537. CpuOverheadFraction: 3,
  538. RamOverheadFraction: 7,
  539. OverheadCostFraction: 6,
  540. },
  541. },
  542. },
  543. }
  544. for _, c := range cases {
  545. t.Run(c.name, func(t *testing.T) {
  546. result := c.input.Clone()
  547. if !result.Equal(c.input) {
  548. t.Errorf("clone result doesn't equal input")
  549. }
  550. })
  551. }
  552. }
  553. func TestNode_MarshalJSON(t *testing.T) {
  554. node := NewNode("node", "cluster", "providerID", *windows[0].start, *windows[0].end, windows[0])
  555. node.SetLabels(AssetLabels{
  556. "label": "value",
  557. })
  558. node.CPUCost = 9.0
  559. node.RAMCost = 0.0
  560. node.RAMCost = 21.0
  561. node.CPUCoreHours = 123.0
  562. node.RAMByteHours = 13323.0
  563. node.GPUHours = 123.0
  564. node.SetAdjustment(1.0)
  565. _, err := json.Marshal(node)
  566. if err != nil {
  567. t.Fatalf("Node.MarshalJSON: unexpected error: %s", err)
  568. }
  569. }
  570. func TestClusterManagement_Add(t *testing.T) {
  571. cm1 := NewClusterManagement(GCPProvider, "cluster1", windows[0])
  572. cm1.Cost = 9.0
  573. cm2 := NewClusterManagement(GCPProvider, "cluster1", windows[0])
  574. cm2.Cost = 4.0
  575. cm3 := cm1.Add(cm2)
  576. // Check that the sums and properties are correct
  577. if cm3.TotalCost() != 13.0 {
  578. t.Fatalf("ClusterManagement.Add: expected %f; got %f", 13.0, cm3.TotalCost())
  579. }
  580. if cm3.GetProperties().Cluster != "cluster1" {
  581. t.Fatalf("ClusterManagement.Add: expected %s; got %s", "cluster1", cm3.GetProperties().Cluster)
  582. }
  583. if cm3.Type() != ClusterManagementAssetType {
  584. t.Fatalf("ClusterManagement.Add: expected %s; got %s", ClusterManagementAssetType, cm3.Type())
  585. }
  586. // Check that the original assets are unchanged
  587. if cm1.TotalCost() != 9.0 {
  588. t.Fatalf("ClusterManagement.Add: expected %f; got %f", 9.0, cm1.TotalCost())
  589. }
  590. if cm2.TotalCost() != 4.0 {
  591. t.Fatalf("ClusterManagement.Add: expected %f; got %f", 4.0, cm2.TotalCost())
  592. }
  593. }
  594. func TestClusterManagement_Clone(t *testing.T) {
  595. // TODO
  596. }
  597. func TestCloudAny_Add(t *testing.T) {
  598. ca1 := NewCloud(ComputeCategory, "ca1", *windows[0].start, *windows[0].end, windows[0])
  599. ca1.Cost = 9.0
  600. ca1.SetAdjustment(1.0)
  601. ca2 := NewCloud(StorageCategory, "ca2", *windows[0].start, *windows[0].end, windows[0])
  602. ca2.Cost = 4.0
  603. ca2.SetAdjustment(1.0)
  604. ca3 := ca1.Add(ca2)
  605. // Check that the sums and properties are correct
  606. if ca3.TotalCost() != 15.0 {
  607. t.Fatalf("Any.Add: expected %f; got %f", 15.0, ca3.TotalCost())
  608. }
  609. if ca3.GetAdjustment() != 2.0 {
  610. t.Fatalf("Any.Add: expected %f; got %f", 2.0, ca3.GetAdjustment())
  611. }
  612. if ca3.Type() != CloudAssetType {
  613. t.Fatalf("Any.Add: expected %s; got %s", CloudAssetType, ca3.Type())
  614. }
  615. // Check that the original assets are unchanged
  616. if ca1.TotalCost() != 10.0 {
  617. t.Fatalf("Any.Add: expected %f; got %f", 10.0, ca1.TotalCost())
  618. }
  619. if ca1.Adjustment != 1.0 {
  620. t.Fatalf("Any.Add: expected %f; got %f", 1.0, ca1.Adjustment)
  621. }
  622. if ca2.TotalCost() != 5.0 {
  623. t.Fatalf("Any.Add: expected %f; got %f", 5.0, ca2.TotalCost())
  624. }
  625. if ca2.Adjustment != 1.0 {
  626. t.Fatalf("Any.Add: expected %f; got %f", 1.0, ca2.Adjustment)
  627. }
  628. }
  629. func TestCloudAny_Clone(t *testing.T) {
  630. // TODO
  631. }
  632. func TestAssetSet_AggregateBy(t *testing.T) {
  633. endYesterday := time.Now().UTC().Truncate(day)
  634. startYesterday := endYesterday.Add(-day)
  635. window := NewWindow(&startYesterday, &endYesterday)
  636. // Scenarios to test:
  637. // 1 Single-aggregation
  638. // 1a []AssetProperty=[Cluster]
  639. // 1b []AssetProperty=[Type]
  640. // 1c []AssetProperty=[Nil]
  641. // 1d []AssetProperty=nil
  642. // 1e aggregateBy []string=["label:test"]
  643. // 2 Multi-aggregation
  644. // 2a []AssetProperty=[Cluster,Type]
  645. // 3 Share resources
  646. // 3a Shared hourly cost > 0.0
  647. // Definitions and set-up:
  648. var as *AssetSet
  649. var err error
  650. // Tests:
  651. // 1 Single-aggregation
  652. // 1a []AssetProperty=[Cluster]
  653. as = GenerateMockAssetSet(startYesterday, day)
  654. err = as.AggregateBy([]string{string(AssetClusterProp)}, nil)
  655. if err != nil {
  656. t.Fatalf("AssetSet.AggregateBy: unexpected error: %s", err)
  657. }
  658. assertAssetSet(t, as, "1a", window, map[string]float64{
  659. "cluster1": 26.0,
  660. "cluster2": 15.0,
  661. "cluster3": 19.0,
  662. }, nil)
  663. // 1b []AssetProperty=[Type]
  664. as = GenerateMockAssetSet(startYesterday, day)
  665. err = as.AggregateBy([]string{string(AssetTypeProp)}, nil)
  666. if err != nil {
  667. t.Fatalf("AssetSet.AggregateBy: unexpected error: %s", err)
  668. }
  669. assertAssetSet(t, as, "1b", window, map[string]float64{
  670. "Node": 49.0,
  671. "Disk": 8.0,
  672. "ClusterManagement": 3.0,
  673. }, nil)
  674. // 1c []AssetProperty=[Nil]
  675. as = GenerateMockAssetSet(startYesterday, day)
  676. err = as.AggregateBy([]string{}, nil)
  677. if err != nil {
  678. t.Fatalf("AssetSet.AggregateBy: unexpected error: %s", err)
  679. }
  680. assertAssetSet(t, as, "1c", window, map[string]float64{
  681. "": 60.0,
  682. }, nil)
  683. // 1d []AssetProperty=nil
  684. as = GenerateMockAssetSet(startYesterday, day)
  685. err = as.AggregateBy(nil, nil)
  686. if err != nil {
  687. t.Fatalf("AssetSet.AggregateBy: unexpected error: %s", err)
  688. }
  689. assertAssetSet(t, as, "1d", window, map[string]float64{
  690. "__undefined__/__undefined__/__undefined__/Compute/cluster1/Node/Kubernetes/gcp-node1/node1": 7.00,
  691. "__undefined__/__undefined__/__undefined__/Compute/cluster1/Node/Kubernetes/gcp-node2/node2": 5.50,
  692. "__undefined__/__undefined__/__undefined__/Compute/cluster1/Node/Kubernetes/gcp-node3/node3": 6.50,
  693. "__undefined__/__undefined__/__undefined__/Storage/cluster1/Disk/Kubernetes/gcp-disk1/disk1": 2.50,
  694. "__undefined__/__undefined__/__undefined__/Storage/cluster1/Disk/Kubernetes/gcp-disk2/disk2": 1.50,
  695. "GCP/__undefined__/__undefined__/Management/cluster1/ClusterManagement/Kubernetes/__undefined__/__undefined__": 3.00,
  696. "__undefined__/__undefined__/__undefined__/Compute/cluster2/Node/Kubernetes/gcp-node4/node4": 11.00,
  697. "__undefined__/__undefined__/__undefined__/Storage/cluster2/Disk/Kubernetes/gcp-disk3/disk3": 2.50,
  698. "__undefined__/__undefined__/__undefined__/Storage/cluster2/Disk/Kubernetes/gcp-disk4/disk4": 1.50,
  699. "GCP/__undefined__/__undefined__/Management/cluster2/ClusterManagement/Kubernetes/__undefined__/__undefined__": 0.00,
  700. "__undefined__/__undefined__/__undefined__/Compute/cluster3/Node/Kubernetes/aws-node5/node5": 19.00,
  701. }, nil)
  702. // 1e aggregateBy []string=["label:test"]
  703. as = GenerateMockAssetSet(startYesterday, day)
  704. err = as.AggregateBy([]string{"label:test"}, nil)
  705. if err != nil {
  706. t.Fatalf("AssetSet.AggregateBy: unexpected error: %s", err)
  707. }
  708. assertAssetSet(t, as, "1e", window, map[string]float64{
  709. "__undefined__": 53.00,
  710. "test=test": 7.00,
  711. }, nil)
  712. // 2 Multi-aggregation
  713. // 2a []AssetProperty=[Cluster,Type]
  714. as = GenerateMockAssetSet(startYesterday, day)
  715. err = as.AggregateBy([]string{string(AssetClusterProp), string(AssetTypeProp)}, nil)
  716. if err != nil {
  717. t.Fatalf("AssetSet.AggregateBy: unexpected error: %s", err)
  718. }
  719. assertAssetSet(t, as, "2a", window, map[string]float64{
  720. "cluster1/Node": 19.0,
  721. "cluster1/Disk": 4.0,
  722. "cluster1/ClusterManagement": 3.0,
  723. "cluster2/Node": 11.0,
  724. "cluster2/Disk": 4.0,
  725. "cluster2/ClusterManagement": 0.0,
  726. "cluster3/Node": 19.0,
  727. }, nil)
  728. // 3 Share resources
  729. // 3a Shared hourly cost > 0.0
  730. as = GenerateMockAssetSet(startYesterday, day)
  731. err = as.AggregateBy([]string{string(AssetTypeProp)}, &AssetAggregationOptions{
  732. SharedHourlyCosts: map[string]float64{"shared1": 0.5},
  733. })
  734. if err != nil {
  735. t.Fatalf("AssetSet.AggregateBy: unexpected error: %s", err)
  736. }
  737. assertAssetSet(t, as, "1a", window, map[string]float64{
  738. "Node": 49.0,
  739. "Disk": 8.0,
  740. "ClusterManagement": 3.0,
  741. "Shared": 12.0,
  742. }, nil)
  743. }
  744. func TestAssetSet_FindMatch(t *testing.T) {
  745. endYesterday := time.Now().UTC().Truncate(day)
  746. startYesterday := endYesterday.Add(-day)
  747. s, e := startYesterday, endYesterday
  748. w := NewWindow(&s, &e)
  749. var query, match Asset
  750. var as *AssetSet
  751. var err error
  752. // Assert success of a simple match of Type and ProviderID
  753. as = GenerateMockAssetSet(startYesterday, day)
  754. query = NewNode("", "", "gcp-node3", s, e, w)
  755. match, err = as.FindMatch(query, []string{string(AssetTypeProp), string(AssetProviderIDProp)}, nil)
  756. if err != nil {
  757. t.Fatalf("AssetSet.FindMatch: unexpected error: %s", err)
  758. }
  759. // Assert error of a simple non-match of Type and ProviderID
  760. as = GenerateMockAssetSet(startYesterday, day)
  761. query = NewNode("", "", "aws-node3", s, e, w)
  762. match, err = as.FindMatch(query, []string{string(AssetTypeProp), string(AssetProviderIDProp)}, nil)
  763. if err == nil {
  764. t.Fatalf("AssetSet.FindMatch: expected error (no match); found %s", match)
  765. }
  766. // Assert error of matching ProviderID, but not Type
  767. as = GenerateMockAssetSet(startYesterday, day)
  768. query = NewCloud(ComputeCategory, "gcp-node3", s, e, w)
  769. match, err = as.FindMatch(query, []string{string(AssetTypeProp), string(AssetProviderIDProp)}, nil)
  770. if err == nil {
  771. t.Fatalf("AssetSet.FindMatch: expected error (no match); found %s", match)
  772. }
  773. }
  774. // Asserts that all Assets within an AssetSet have a Window that
  775. // matches that of the AssetSet.
  776. func TestAssetSet_InsertMatchingWindow(t *testing.T) {
  777. setStart := time.Now().Round(time.Hour)
  778. setEnd := setStart.Add(1 * time.Hour)
  779. a1WindowStart := setStart.Add(5 * time.Minute)
  780. a1WindowEnd := setStart.Add(50 * time.Minute)
  781. a2WindowStart := setStart.Add(17 * time.Minute)
  782. a2WindowEnd := setStart.Add(34 * time.Minute)
  783. a1 := &Node{}
  784. a1.SetProperties(&AssetProperties{
  785. Name: "asset-1",
  786. })
  787. a1.Window = NewClosedWindow(a1WindowStart, a1WindowEnd)
  788. a2 := &Disk{}
  789. a2.SetProperties(&AssetProperties{
  790. Name: "asset-2",
  791. })
  792. a2.Window = NewClosedWindow(a2WindowStart, a2WindowEnd)
  793. as := NewAssetSet(setStart, setEnd)
  794. as.Insert(a1, nil)
  795. as.Insert(a2, nil)
  796. if as.Length() != 2 {
  797. t.Errorf("AS length got %d, expected %d", as.Length(), 2)
  798. }
  799. for _, a := range as.Assets {
  800. if !(*a.GetWindow().Start()).Equal(setStart) {
  801. t.Errorf("Asset %s window start is %s, expected %s", a.GetProperties().Name, *a.GetWindow().Start(), setStart)
  802. }
  803. if !(*a.GetWindow().End()).Equal(setEnd) {
  804. t.Errorf("Asset %s window end is %s, expected %s", a.GetProperties().Name, *a.GetWindow().End(), setEnd)
  805. }
  806. }
  807. }
  808. func TestAssetSetRange_AccumulateToAssetSet(t *testing.T) {
  809. endYesterday := time.Now().UTC().Truncate(day)
  810. startYesterday := endYesterday.Add(-day)
  811. startD2 := startYesterday
  812. startD1 := startD2.Add(-day)
  813. startD0 := startD1.Add(-day)
  814. window := NewWindow(&startD0, &endYesterday)
  815. var asr *AssetSetRange
  816. var as *AssetSet
  817. var err error
  818. asr = NewAssetSetRange(
  819. GenerateMockAssetSet(startD0, day),
  820. GenerateMockAssetSet(startD1, day),
  821. GenerateMockAssetSet(startD2, day),
  822. )
  823. err = asr.AggregateBy(nil, nil)
  824. if err != nil {
  825. t.Fatalf("AssetSetRange.AggregateBy: unexpected error: %s", err)
  826. }
  827. as, err = asr.AccumulateToAssetSet()
  828. if err != nil {
  829. t.Fatalf("AssetSetRange.AccumulateToAssetSet: unexpected error: %s", err)
  830. }
  831. assertAssetSet(t, as, "1a", window, map[string]float64{
  832. "__undefined__/__undefined__/__undefined__/Compute/cluster1/Node/Kubernetes/gcp-node1/node1": 21.00,
  833. "__undefined__/__undefined__/__undefined__/Compute/cluster1/Node/Kubernetes/gcp-node2/node2": 16.50,
  834. "__undefined__/__undefined__/__undefined__/Compute/cluster1/Node/Kubernetes/gcp-node3/node3": 19.50,
  835. "__undefined__/__undefined__/__undefined__/Storage/cluster1/Disk/Kubernetes/gcp-disk1/disk1": 7.50,
  836. "__undefined__/__undefined__/__undefined__/Storage/cluster1/Disk/Kubernetes/gcp-disk2/disk2": 4.50,
  837. "GCP/__undefined__/__undefined__/Management/cluster1/ClusterManagement/Kubernetes/__undefined__/__undefined__": 9.00,
  838. "__undefined__/__undefined__/__undefined__/Compute/cluster2/Node/Kubernetes/gcp-node4/node4": 33.00,
  839. "__undefined__/__undefined__/__undefined__/Storage/cluster2/Disk/Kubernetes/gcp-disk3/disk3": 7.50,
  840. "__undefined__/__undefined__/__undefined__/Storage/cluster2/Disk/Kubernetes/gcp-disk4/disk4": 4.50,
  841. "GCP/__undefined__/__undefined__/Management/cluster2/ClusterManagement/Kubernetes/__undefined__/__undefined__": 0.00,
  842. "__undefined__/__undefined__/__undefined__/Compute/cluster3/Node/Kubernetes/aws-node5/node5": 57.00,
  843. }, nil)
  844. asr = NewAssetSetRange(
  845. GenerateMockAssetSet(startD0, day),
  846. GenerateMockAssetSet(startD1, day),
  847. GenerateMockAssetSet(startD2, day),
  848. )
  849. err = asr.AggregateBy([]string{}, nil)
  850. if err != nil {
  851. t.Fatalf("AssetSetRange.AggregateBy: unexpected error: %s", err)
  852. }
  853. as, err = asr.AccumulateToAssetSet()
  854. if err != nil {
  855. t.Fatalf("AssetSetRange.AccumulateToAssetSet: unexpected error: %s", err)
  856. }
  857. assertAssetSet(t, as, "1b", window, map[string]float64{
  858. "": 180.00,
  859. }, nil)
  860. asr = NewAssetSetRange(
  861. GenerateMockAssetSet(startD0, day),
  862. GenerateMockAssetSet(startD1, day),
  863. GenerateMockAssetSet(startD2, day),
  864. )
  865. err = asr.AggregateBy([]string{string(AssetTypeProp)}, nil)
  866. if err != nil {
  867. t.Fatalf("AssetSetRange.AggregateBy: unexpected error: %s", err)
  868. }
  869. as, err = asr.AccumulateToAssetSet()
  870. if err != nil {
  871. t.Fatalf("AssetSetRange.AggregateBy: unexpected error: %s", err)
  872. }
  873. assertAssetSet(t, as, "1c", window, map[string]float64{
  874. "Node": 147.0,
  875. "Disk": 24.0,
  876. "ClusterManagement": 9.0,
  877. }, nil)
  878. asr = NewAssetSetRange(
  879. GenerateMockAssetSet(startD0, day),
  880. GenerateMockAssetSet(startD1, day),
  881. GenerateMockAssetSet(startD2, day),
  882. )
  883. err = asr.AggregateBy([]string{string(AssetClusterProp)}, nil)
  884. if err != nil {
  885. t.Fatalf("AssetSetRange.AggregateBy: unexpected error: %s", err)
  886. }
  887. as, err = asr.AccumulateToAssetSet()
  888. if err != nil {
  889. t.Fatalf("AssetSetRange.AggregateBy: unexpected error: %s", err)
  890. }
  891. assertAssetSet(t, as, "1c", window, map[string]float64{
  892. "cluster1": 78.0,
  893. "cluster2": 45.0,
  894. "cluster3": 57.0,
  895. }, nil)
  896. // Accumulation with aggregation should work, even when the first AssetSet
  897. // is empty (this was previously an issue)
  898. asr = NewAssetSetRange(
  899. NewAssetSet(startD0, startD1),
  900. GenerateMockAssetSet(startD1, day),
  901. GenerateMockAssetSet(startD2, day),
  902. )
  903. err = asr.AggregateBy([]string{string(AssetTypeProp)}, nil)
  904. if err != nil {
  905. t.Fatalf("AssetSetRange.AggregateBy: unexpected error: %s", err)
  906. }
  907. as, err = asr.AccumulateToAssetSet()
  908. if err != nil {
  909. t.Fatalf("AssetSetRange.AggregateBy: unexpected error: %s", err)
  910. }
  911. assertAssetSet(t, as, "1d", window, map[string]float64{
  912. "Node": 98.00,
  913. "Disk": 16.00,
  914. "ClusterManagement": 6.00,
  915. }, nil)
  916. }
  917. func TestAssetSetRange_Start(t *testing.T) {
  918. tests := []struct {
  919. name string
  920. arg *AssetSetRange
  921. expectError bool
  922. expected time.Time
  923. }{
  924. {
  925. name: "Empty ASR",
  926. arg: nil,
  927. expectError: true,
  928. },
  929. {
  930. name: "Single asset",
  931. arg: &AssetSetRange{
  932. Assets: []*AssetSet{
  933. {
  934. Assets: map[string]Asset{
  935. "a": &Node{
  936. Start: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  937. },
  938. },
  939. },
  940. },
  941. },
  942. expected: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  943. },
  944. {
  945. name: "Two assets",
  946. arg: &AssetSetRange{
  947. Assets: []*AssetSet{
  948. {
  949. Assets: map[string]Asset{
  950. "a": &Node{
  951. Start: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  952. },
  953. "b": &Node{
  954. Start: time.Date(1970, 1, 2, 0, 0, 0, 0, time.UTC),
  955. },
  956. },
  957. },
  958. },
  959. },
  960. expected: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  961. },
  962. {
  963. name: "Two AssetSets",
  964. arg: &AssetSetRange{
  965. Assets: []*AssetSet{
  966. {
  967. Assets: map[string]Asset{
  968. "a": &Node{
  969. Start: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  970. },
  971. },
  972. },
  973. {
  974. Assets: map[string]Asset{
  975. "b": &Node{
  976. Start: time.Date(1970, 1, 2, 0, 0, 0, 0, time.UTC),
  977. },
  978. },
  979. },
  980. },
  981. },
  982. expected: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  983. },
  984. }
  985. for _, test := range tests {
  986. result, err := test.arg.Start()
  987. if test.expectError && err != nil {
  988. continue
  989. }
  990. if test.expectError && err == nil {
  991. t.Errorf("%s: expected error and got none", test.name)
  992. } else if result != test.expected {
  993. t.Errorf("%s: expected %s but got %s", test.name, test.expected, result)
  994. }
  995. }
  996. }
  997. func TestAssetSetRange_End(t *testing.T) {
  998. tests := []struct {
  999. name string
  1000. arg *AssetSetRange
  1001. expectError bool
  1002. expected time.Time
  1003. }{
  1004. {
  1005. name: "Empty ASR",
  1006. arg: nil,
  1007. expectError: true,
  1008. },
  1009. {
  1010. name: "Single asset",
  1011. arg: &AssetSetRange{
  1012. Assets: []*AssetSet{
  1013. {
  1014. Assets: map[string]Asset{
  1015. "a": &Node{
  1016. End: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  1017. },
  1018. },
  1019. },
  1020. },
  1021. },
  1022. expected: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  1023. },
  1024. {
  1025. name: "Two assets",
  1026. arg: &AssetSetRange{
  1027. Assets: []*AssetSet{
  1028. {
  1029. Assets: map[string]Asset{
  1030. "a": &Node{
  1031. End: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  1032. },
  1033. "b": &Node{
  1034. End: time.Date(1970, 1, 2, 0, 0, 0, 0, time.UTC),
  1035. },
  1036. },
  1037. },
  1038. },
  1039. },
  1040. expected: time.Date(1970, 1, 2, 0, 0, 0, 0, time.UTC),
  1041. },
  1042. {
  1043. name: "Two AssetSets",
  1044. arg: &AssetSetRange{
  1045. Assets: []*AssetSet{
  1046. {
  1047. Assets: map[string]Asset{
  1048. "a": &Node{
  1049. End: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  1050. },
  1051. },
  1052. },
  1053. {
  1054. Assets: map[string]Asset{
  1055. "b": &Node{
  1056. End: time.Date(1970, 1, 2, 0, 0, 0, 0, time.UTC),
  1057. },
  1058. },
  1059. },
  1060. },
  1061. },
  1062. expected: time.Date(1970, 1, 2, 0, 0, 0, 0, time.UTC),
  1063. },
  1064. }
  1065. for _, test := range tests {
  1066. result, err := test.arg.End()
  1067. if test.expectError && err != nil {
  1068. continue
  1069. }
  1070. if test.expectError && err == nil {
  1071. t.Errorf("%s: expected error and got none", test.name)
  1072. } else if result != test.expected {
  1073. t.Errorf("%s: expected %s but got %s", test.name, test.expected, result)
  1074. }
  1075. }
  1076. }
  1077. func TestAssetSetRange_Minutes(t *testing.T) {
  1078. tests := []struct {
  1079. name string
  1080. arg *AssetSetRange
  1081. expected float64
  1082. }{
  1083. {
  1084. name: "Empty ASR",
  1085. arg: nil,
  1086. expected: 0,
  1087. },
  1088. {
  1089. name: "Single asset",
  1090. arg: &AssetSetRange{
  1091. Assets: []*AssetSet{
  1092. {
  1093. Assets: map[string]Asset{
  1094. "a": &Node{
  1095. Start: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  1096. End: time.Date(1970, 1, 2, 0, 0, 0, 0, time.UTC),
  1097. },
  1098. },
  1099. },
  1100. },
  1101. },
  1102. expected: 24 * 60,
  1103. },
  1104. {
  1105. name: "Two assets",
  1106. arg: &AssetSetRange{
  1107. Assets: []*AssetSet{
  1108. {
  1109. Assets: map[string]Asset{
  1110. "a": &Node{
  1111. Start: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  1112. End: time.Date(1970, 1, 2, 0, 0, 0, 0, time.UTC),
  1113. },
  1114. "b": &Node{
  1115. Start: time.Date(1970, 1, 2, 0, 0, 0, 0, time.UTC),
  1116. End: time.Date(1970, 1, 3, 0, 0, 0, 0, time.UTC),
  1117. },
  1118. },
  1119. },
  1120. },
  1121. },
  1122. expected: 2 * 24 * 60,
  1123. },
  1124. {
  1125. name: "Two AssetSets",
  1126. arg: &AssetSetRange{
  1127. Assets: []*AssetSet{
  1128. {
  1129. Assets: map[string]Asset{
  1130. "a": &Node{
  1131. Start: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
  1132. End: time.Date(1970, 1, 2, 0, 0, 0, 0, time.UTC),
  1133. },
  1134. },
  1135. },
  1136. {
  1137. Assets: map[string]Asset{
  1138. "b": &Node{
  1139. Start: time.Date(1970, 1, 2, 0, 0, 0, 0, time.UTC),
  1140. End: time.Date(1970, 1, 3, 0, 0, 0, 0, time.UTC),
  1141. },
  1142. },
  1143. },
  1144. },
  1145. },
  1146. expected: 2 * 24 * 60,
  1147. },
  1148. }
  1149. for _, test := range tests {
  1150. result := test.arg.Minutes()
  1151. if result != test.expected {
  1152. t.Errorf("%s: expected %f but got %f", test.name, test.expected, result)
  1153. }
  1154. }
  1155. }
  1156. func TestAssetSetRange_MarshalJSON(t *testing.T) {
  1157. tests := []struct {
  1158. name string
  1159. arg *AssetSetRange
  1160. expected *AssetSetRange
  1161. }{
  1162. {
  1163. name: "Nil ASR",
  1164. arg: nil,
  1165. },
  1166. {
  1167. name: "Nil AS in ASR",
  1168. arg: NewAssetSetRange(nil),
  1169. },
  1170. {
  1171. name: "Normal ASR",
  1172. arg: &AssetSetRange{
  1173. Assets: []*AssetSet{
  1174. {
  1175. Assets: map[string]Asset{
  1176. "a": &Any{
  1177. Start: time.Now().UTC().Truncate(day),
  1178. },
  1179. },
  1180. },
  1181. },
  1182. },
  1183. },
  1184. }
  1185. for _, test := range tests {
  1186. bytes, err := json.Marshal(test.arg)
  1187. if err != nil {
  1188. t.Fatalf("ASR Marshal: test %s, unexpected error: %s", test.name, err)
  1189. }
  1190. var testASR []*AssetSet
  1191. marshaled := &testASR
  1192. err = json.Unmarshal(bytes, marshaled)
  1193. if err != nil {
  1194. t.Fatalf("ASR Unmarshal: test %s: unexpected error: %s", test.name, err)
  1195. }
  1196. if test.arg.Length() != len(testASR) {
  1197. t.Fatalf("ASR Unmarshal: test %s: length mutated in encoding: expected %d but got %d", test.name, test.arg.Length(), len(testASR))
  1198. }
  1199. // asset don't unmarshal back from json
  1200. }
  1201. }
  1202. func TestAssetSetRange_AccumulateBy_None(t *testing.T) {
  1203. ago4d := time.Now().UTC().Truncate(day).Add(-4 * day)
  1204. ago3d := time.Now().UTC().Truncate(day).Add(-3 * day)
  1205. ago2d := time.Now().UTC().Truncate(day).Add(-2 * day)
  1206. yesterday := time.Now().UTC().Truncate(day).Add(-day)
  1207. today := time.Now().UTC().Truncate(day)
  1208. ago4dAS := GenerateMockAssetSet(ago4d, day)
  1209. ago3dAS := GenerateMockAssetSet(ago3d, day)
  1210. ago2dAS := GenerateMockAssetSet(ago2d, day)
  1211. yesterdayAS := GenerateMockAssetSet(yesterday, day)
  1212. todayAS := GenerateMockAssetSet(today, day)
  1213. asr := NewAssetSetRange(ago4dAS, ago3dAS, ago2dAS, yesterdayAS, todayAS)
  1214. asr, err := asr.Accumulate(AccumulateOptionNone)
  1215. if err != nil {
  1216. t.Fatalf("unexpected error calling accumulateBy: %s", err)
  1217. }
  1218. if len(asr.Assets) != 5 {
  1219. t.Fatalf("expected 5 asset sets, got:%d", len(asr.Assets))
  1220. }
  1221. }
  1222. func TestAssetSetRange_AccumulateBy_All(t *testing.T) {
  1223. ago4d := time.Now().UTC().Truncate(day).Add(-4 * day)
  1224. ago3d := time.Now().UTC().Truncate(day).Add(-3 * day)
  1225. ago2d := time.Now().UTC().Truncate(day).Add(-2 * day)
  1226. yesterday := time.Now().UTC().Truncate(day).Add(-day)
  1227. today := time.Now().UTC().Truncate(day)
  1228. ago4dAS := GenerateMockAssetSet(ago4d, day)
  1229. ago3dAS := GenerateMockAssetSet(ago3d, day)
  1230. ago2dAS := GenerateMockAssetSet(ago2d, day)
  1231. yesterdayAS := GenerateMockAssetSet(yesterday, day)
  1232. todayAS := GenerateMockAssetSet(today, day)
  1233. asr := NewAssetSetRange(ago4dAS, ago3dAS, ago2dAS, yesterdayAS, todayAS)
  1234. asr, err := asr.Accumulate(AccumulateOptionAll)
  1235. if err != nil {
  1236. t.Fatalf("unexpected error calling accumulateBy: %s", err)
  1237. }
  1238. if len(asr.Assets) != 1 {
  1239. t.Fatalf("expected 1 asset set, got:%d", len(asr.Assets))
  1240. }
  1241. }
  1242. func TestAssetSetRange_AccumulateBy_Hour(t *testing.T) {
  1243. ago4h := time.Now().UTC().Truncate(time.Hour).Add(-4 * time.Hour)
  1244. ago3h := time.Now().UTC().Truncate(time.Hour).Add(-3 * time.Hour)
  1245. ago2h := time.Now().UTC().Truncate(time.Hour).Add(-2 * time.Hour)
  1246. ago1h := time.Now().UTC().Truncate(time.Hour).Add(-time.Hour)
  1247. currentHour := time.Now().UTC().Truncate(time.Hour)
  1248. ago4hAS := GenerateMockAssetSet(ago4h, time.Hour)
  1249. ago3hAS := GenerateMockAssetSet(ago3h, time.Hour)
  1250. ago2hAS := GenerateMockAssetSet(ago2h, time.Hour)
  1251. ago1hAS := GenerateMockAssetSet(ago1h, time.Hour)
  1252. currentHourAS := GenerateMockAssetSet(currentHour, time.Hour)
  1253. asr := NewAssetSetRange(ago4hAS, ago3hAS, ago2hAS, ago1hAS, currentHourAS)
  1254. asr, err := asr.Accumulate(AccumulateOptionHour)
  1255. if err != nil {
  1256. t.Fatalf("unexpected error calling accumulateBy: %s", err)
  1257. }
  1258. if len(asr.Assets) != 5 {
  1259. t.Fatalf("expected 5 asset sets, got:%d", len(asr.Assets))
  1260. }
  1261. allocMap := asr.Assets[0].Assets
  1262. alloc := allocMap["__undefined__/__undefined__/__undefined__/Storage/cluster2/Disk/Kubernetes/gcp-disk4/disk4"]
  1263. if alloc.Minutes() != 60.0 {
  1264. t.Errorf("accumulating asset set range: expected %f minutes; actual %f", 60.0, alloc.Minutes())
  1265. }
  1266. }
  1267. func TestAssetSetRange_AccumulateBy_Day_From_Day(t *testing.T) {
  1268. ago4d := time.Now().UTC().Truncate(day).Add(-4 * day)
  1269. ago3d := time.Now().UTC().Truncate(day).Add(-3 * day)
  1270. ago2d := time.Now().UTC().Truncate(day).Add(-2 * day)
  1271. yesterday := time.Now().UTC().Truncate(day).Add(-day)
  1272. today := time.Now().UTC().Truncate(day)
  1273. ago4dAS := GenerateMockAssetSet(ago4d, day)
  1274. ago3dAS := GenerateMockAssetSet(ago3d, day)
  1275. ago2dAS := GenerateMockAssetSet(ago2d, day)
  1276. yesterdayAS := GenerateMockAssetSet(yesterday, day)
  1277. todayAS := GenerateMockAssetSet(today, day)
  1278. asr := NewAssetSetRange(ago4dAS, ago3dAS, ago2dAS, yesterdayAS, todayAS)
  1279. asr, err := asr.Accumulate(AccumulateOptionDay)
  1280. if err != nil {
  1281. t.Fatalf("unexpected error calling accumulateBy: %s", err)
  1282. }
  1283. if len(asr.Assets) != 5 {
  1284. t.Fatalf("expected 5 asset sets, got:%d", len(asr.Assets))
  1285. }
  1286. allocMap := asr.Assets[0].Assets
  1287. alloc := allocMap["__undefined__/__undefined__/__undefined__/Storage/cluster2/Disk/Kubernetes/gcp-disk4/disk4"]
  1288. if alloc.Minutes() != 1440.0 {
  1289. t.Errorf("accumulating asset set range: expected %f minutes; actual %f", 1440.0, alloc.Minutes())
  1290. }
  1291. }
  1292. func TestAssetSetRange_AccumulateBy_Day_From_Hours(t *testing.T) {
  1293. ago4h := time.Now().UTC().Truncate(time.Hour).Add(-4 * time.Hour)
  1294. ago3h := time.Now().UTC().Truncate(time.Hour).Add(-3 * time.Hour)
  1295. ago2h := time.Now().UTC().Truncate(time.Hour).Add(-2 * time.Hour)
  1296. ago1h := time.Now().UTC().Truncate(time.Hour).Add(-time.Hour)
  1297. currentHour := time.Now().UTC().Truncate(time.Hour)
  1298. ago4hAS := GenerateMockAssetSet(ago4h, time.Hour)
  1299. ago3hAS := GenerateMockAssetSet(ago3h, time.Hour)
  1300. ago2hAS := GenerateMockAssetSet(ago2h, time.Hour)
  1301. ago1hAS := GenerateMockAssetSet(ago1h, time.Hour)
  1302. currentHourAS := GenerateMockAssetSet(currentHour, time.Hour)
  1303. asr := NewAssetSetRange(ago4hAS, ago3hAS, ago2hAS, ago1hAS, currentHourAS)
  1304. asr, err := asr.Accumulate(AccumulateOptionDay)
  1305. if err != nil {
  1306. t.Fatalf("unexpected error calling accumulateBy: %s", err)
  1307. }
  1308. if len(asr.Assets) != 1 && len(asr.Assets) != 2 {
  1309. t.Fatalf("expected 1 allocation set, got:%d", len(asr.Assets))
  1310. }
  1311. allocMap := asr.Assets[0].Assets
  1312. alloc := allocMap["__undefined__/__undefined__/__undefined__/Storage/cluster2/Disk/Kubernetes/gcp-disk4/disk4"]
  1313. if alloc.Minutes() > 300.0 {
  1314. t.Errorf("accumulating AllocationSetRange: expected %f or less minutes; actual %f", 300.0, alloc.Minutes())
  1315. }
  1316. }
  1317. func TestAssetSetRange_AccumulateBy_Week(t *testing.T) {
  1318. ago9d := time.Now().UTC().Truncate(day).Add(-9 * day)
  1319. ago8d := time.Now().UTC().Truncate(day).Add(-8 * day)
  1320. ago7d := time.Now().UTC().Truncate(day).Add(-7 * day)
  1321. ago6d := time.Now().UTC().Truncate(day).Add(-6 * day)
  1322. ago5d := time.Now().UTC().Truncate(day).Add(-5 * day)
  1323. ago4d := time.Now().UTC().Truncate(day).Add(-4 * day)
  1324. ago3d := time.Now().UTC().Truncate(day).Add(-3 * day)
  1325. ago2d := time.Now().UTC().Truncate(day).Add(-2 * day)
  1326. yesterday := time.Now().UTC().Truncate(day).Add(-day)
  1327. today := time.Now().UTC().Truncate(day)
  1328. ago9dAS := GenerateMockAssetSet(ago9d, day)
  1329. ago8dAS := GenerateMockAssetSet(ago8d, day)
  1330. ago7dAS := GenerateMockAssetSet(ago7d, day)
  1331. ago6dAS := GenerateMockAssetSet(ago6d, day)
  1332. ago5dAS := GenerateMockAssetSet(ago5d, day)
  1333. ago4dAS := GenerateMockAssetSet(ago4d, day)
  1334. ago3dAS := GenerateMockAssetSet(ago3d, day)
  1335. ago2dAS := GenerateMockAssetSet(ago2d, day)
  1336. yesterdayAS := GenerateMockAssetSet(yesterday, day)
  1337. todayAS := GenerateMockAssetSet(today, day)
  1338. asr := NewAssetSetRange(ago9dAS, ago8dAS, ago7dAS, ago6dAS, ago5dAS, ago4dAS, ago3dAS, ago2dAS, yesterdayAS, todayAS)
  1339. asr, err := asr.Accumulate(AccumulateOptionWeek)
  1340. if err != nil {
  1341. t.Fatalf("unexpected error calling accumulateBy: %s", err)
  1342. }
  1343. if len(asr.Assets) != 2 && len(asr.Assets) != 3 {
  1344. t.Fatalf("expected 2 or 3 asset sets, got:%d", len(asr.Assets))
  1345. }
  1346. for _, as := range asr.Assets {
  1347. if as.Window.Duration() < time.Hour*24 || as.Window.Duration() > time.Hour*24*7 {
  1348. t.Fatalf("expected window duration to be between 1 and 7 days, got:%s", as.Window.Duration().String())
  1349. }
  1350. }
  1351. }
  1352. func TestAssetSetRange_AccumulateBy_Month(t *testing.T) {
  1353. prevMonth1stDay := time.Date(2020, 01, 29, 0, 0, 0, 0, time.UTC)
  1354. prevMonth2ndDay := time.Date(2020, 01, 30, 0, 0, 0, 0, time.UTC)
  1355. prevMonth3ndDay := time.Date(2020, 01, 31, 0, 0, 0, 0, time.UTC)
  1356. nextMonth1stDay := time.Date(2020, 02, 01, 0, 0, 0, 0, time.UTC)
  1357. prev1AS := GenerateMockAssetSet(prevMonth1stDay, day)
  1358. prev2AS := GenerateMockAssetSet(prevMonth2ndDay, day)
  1359. prev3AS := GenerateMockAssetSet(prevMonth3ndDay, day)
  1360. nextAS := GenerateMockAssetSet(nextMonth1stDay, day)
  1361. asr := NewAssetSetRange(prev1AS, prev2AS, prev3AS, nextAS)
  1362. asr, err := asr.Accumulate(AccumulateOptionMonth)
  1363. if err != nil {
  1364. t.Fatalf("unexpected error calling accumulateBy: %s", err)
  1365. }
  1366. if len(asr.Assets) != 2 {
  1367. t.Fatalf("expected 2 assets sets, got:%d", len(asr.Assets))
  1368. }
  1369. for _, as := range asr.Assets {
  1370. if as.Window.Duration() < time.Hour*24 || as.Window.Duration() > time.Hour*24*31 {
  1371. t.Fatalf("expected window duration to be between 1 and 31 days, got:%s", as.Window.Duration().String())
  1372. }
  1373. }
  1374. }
  1375. func TestAny_SanitizeNaN(t *testing.T) {
  1376. any := getMockAny(math.NaN())
  1377. any.SanitizeNaN()
  1378. v := reflect.ValueOf(any)
  1379. checkAllFloat64sForNaN(t, v, "TestAny_SanitizeNaN")
  1380. }
  1381. func getMockAny(f float64) Any {
  1382. return Any{
  1383. Adjustment: f,
  1384. Cost: f,
  1385. }
  1386. }
  1387. func TestCloud_SanitizeNaN(t *testing.T) {
  1388. cloud := getMockCloud(math.NaN())
  1389. cloud.SanitizeNaN()
  1390. v := reflect.ValueOf(cloud)
  1391. checkAllFloat64sForNaN(t, v, "TestCloud_SanitizeNaN")
  1392. }
  1393. func getMockCloud(f float64) Cloud {
  1394. return Cloud{
  1395. Adjustment: f,
  1396. Cost: f,
  1397. Credit: f,
  1398. }
  1399. }
  1400. func TestClusterManagement_SanitizeNaN(t *testing.T) {
  1401. cm := getMockClusterManagement(math.NaN())
  1402. cm.SanitizeNaN()
  1403. v := reflect.ValueOf(cm)
  1404. checkAllFloat64sForNaN(t, v, "TestClusterManagement_SanitizeNaN")
  1405. }
  1406. func getMockClusterManagement(f float64) ClusterManagement {
  1407. return ClusterManagement{
  1408. Cost: f,
  1409. Adjustment: f,
  1410. }
  1411. }
  1412. func TestDisk_SanitizeNaN(t *testing.T) {
  1413. disk := getMockDisk(math.NaN())
  1414. disk.SanitizeNaN()
  1415. v := reflect.ValueOf(disk)
  1416. checkAllFloat64sForNaN(t, v, "TestDisk_SanitizeNaN")
  1417. vBreakdown := reflect.ValueOf(*disk.Breakdown)
  1418. checkAllFloat64sForNaN(t, vBreakdown, "TestDisk_SanitizeNaN")
  1419. }
  1420. func getMockDisk(f float64) Disk {
  1421. bhu := f
  1422. bum := f
  1423. breakdown := getMockBreakdown(f)
  1424. return Disk{
  1425. Adjustment: f,
  1426. Cost: f,
  1427. ByteHours: f,
  1428. Local: f,
  1429. Breakdown: &breakdown,
  1430. ByteHoursUsed: &bhu,
  1431. ByteUsageMax: &bum,
  1432. }
  1433. }
  1434. func TestBreakdown_SanitizeNaN(t *testing.T) {
  1435. b := getMockBreakdown(math.NaN())
  1436. b.SanitizeNaN()
  1437. v := reflect.ValueOf(b)
  1438. checkAllFloat64sForNaN(t, v, "TestBreakdown_SanitizeNaN")
  1439. }
  1440. func getMockBreakdown(f float64) Breakdown {
  1441. return Breakdown{
  1442. Idle: f,
  1443. Other: f,
  1444. System: f,
  1445. User: f,
  1446. }
  1447. }
  1448. func TestNetwork_SanitizeNaN(t *testing.T) {
  1449. n := getMockNetwork(math.NaN())
  1450. n.SanitizeNaN()
  1451. v := reflect.ValueOf(n)
  1452. checkAllFloat64sForNaN(t, v, "TestNetwork_SanitizeNaN")
  1453. }
  1454. func getMockNetwork(f float64) Network {
  1455. return Network{
  1456. Adjustment: f,
  1457. Cost: f,
  1458. }
  1459. }
  1460. func TestNodeOverhead_SanitizeNaN(t *testing.T) {
  1461. n := getMockNodeOverhead(math.NaN())
  1462. n.SanitizeNaN()
  1463. v := reflect.ValueOf(n)
  1464. checkAllFloat64sForNaN(t, v, "TestNodeOverhead_SanitizeNaN")
  1465. }
  1466. func getMockNodeOverhead(f float64) NodeOverhead {
  1467. return NodeOverhead{
  1468. CpuOverheadFraction: f,
  1469. RamOverheadFraction: f,
  1470. OverheadCostFraction: f,
  1471. }
  1472. }
  1473. func TestNode_SanitizeNaN(t *testing.T) {
  1474. n := getMockNode(math.NaN())
  1475. n.SanitizeNaN()
  1476. v := reflect.ValueOf(n)
  1477. checkAllFloat64sForNaN(t, v, "TestNode_SanitizeNaN")
  1478. vCpu := reflect.ValueOf(*n.CPUBreakdown)
  1479. checkAllFloat64sForNaN(t, vCpu, "TestNode_SanitizeNaN")
  1480. vRam := reflect.ValueOf(*n.RAMBreakdown)
  1481. checkAllFloat64sForNaN(t, vRam, "TestNode_SanitizeNaN")
  1482. vOverhead := reflect.ValueOf(*n.Overhead)
  1483. checkAllFloat64sForNaN(t, vOverhead, "TestNode_SanitizeNaN")
  1484. }
  1485. func getMockNode(f float64) Node {
  1486. cpuBreakdown := getMockBreakdown(f)
  1487. ramBreakdown := getMockBreakdown(f)
  1488. overhead := getMockNodeOverhead(f)
  1489. return Node{
  1490. Adjustment: f,
  1491. CPUCoreHours: f,
  1492. RAMByteHours: f,
  1493. GPUHours: f,
  1494. CPUBreakdown: &cpuBreakdown,
  1495. RAMBreakdown: &ramBreakdown,
  1496. CPUCost: f,
  1497. GPUCost: f,
  1498. GPUCount: f,
  1499. RAMCost: f,
  1500. Discount: f,
  1501. Preemptible: f,
  1502. Overhead: &overhead,
  1503. }
  1504. }
  1505. func TestLoadBalancer_SanitizeNaN(t *testing.T) {
  1506. lb := getMockLoadBalancer(math.NaN())
  1507. lb.SanitizeNaN()
  1508. v := reflect.ValueOf(lb)
  1509. checkAllFloat64sForNaN(t, v, "TestLoadBalancer_SanitizeNaN")
  1510. }
  1511. func getMockLoadBalancer(f float64) LoadBalancer {
  1512. return LoadBalancer{
  1513. Adjustment: f,
  1514. Cost: f,
  1515. }
  1516. }
  1517. func TestSharedAsset_SanitizeNaN(t *testing.T) {
  1518. sa := getMockSharedAsset(math.NaN())
  1519. sa.SanitizeNaN()
  1520. v := reflect.ValueOf(sa)
  1521. checkAllFloat64sForNaN(t, v, "TestSharedAsset_SanitizeNaN")
  1522. }
  1523. func getMockSharedAsset(f float64) SharedAsset {
  1524. return SharedAsset{
  1525. Cost: f,
  1526. }
  1527. }
  1528. func TestAssetSet_SanitizeNaN(t *testing.T) {
  1529. testCaseName := "TestAssetSet_SanitizeNaN"
  1530. as := getMockAssetSet(math.NaN())
  1531. as.SanitizeNaN()
  1532. v := reflect.ValueOf(as)
  1533. checkAllFloat64sForNaN(t, v, testCaseName)
  1534. for _, a := range as.Assets {
  1535. if math.IsNaN(a.TotalCost()) {
  1536. t.Fatalf("TestAssetSet_SanitizeNaN: Asset: expected not NaN for TotalCost(): expected NaN, got:%f", a.TotalCost())
  1537. }
  1538. if math.IsNaN(a.GetAdjustment()) {
  1539. t.Fatalf("TestAssetSet_SanitizeNaN: Asset: expected not NaN for GetAdjustment(): expected NaN, got:%f", a.GetAdjustment())
  1540. }
  1541. }
  1542. for _, any := range as.Any {
  1543. vAny := reflect.ValueOf(*any)
  1544. checkAllFloat64sForNaN(t, vAny, testCaseName)
  1545. }
  1546. for _, cloud := range as.Cloud {
  1547. vCloud := reflect.ValueOf(*cloud)
  1548. checkAllFloat64sForNaN(t, vCloud, testCaseName)
  1549. }
  1550. for _, cm := range as.ClusterManagement {
  1551. vCM := reflect.ValueOf(*cm)
  1552. checkAllFloat64sForNaN(t, vCM, testCaseName)
  1553. }
  1554. for _, disk := range as.Disks {
  1555. vDisk := reflect.ValueOf(*disk)
  1556. checkAllFloat64sForNaN(t, vDisk, testCaseName)
  1557. }
  1558. for _, network := range as.Network {
  1559. vNetwork := reflect.ValueOf(*network)
  1560. checkAllFloat64sForNaN(t, vNetwork, testCaseName)
  1561. }
  1562. for _, node := range as.Nodes {
  1563. vNode := reflect.ValueOf(*node)
  1564. checkAllFloat64sForNaN(t, vNode, testCaseName)
  1565. }
  1566. for _, sa := range as.SharedAssets {
  1567. vSA := reflect.ValueOf(*sa)
  1568. checkAllFloat64sForNaN(t, vSA, testCaseName)
  1569. }
  1570. }
  1571. func getMockAssetSet(f float64) AssetSet {
  1572. any := getMockAny(f)
  1573. cloud := getMockCloud(f)
  1574. cm := getMockClusterManagement(f)
  1575. disk := getMockDisk(f)
  1576. network := getMockNetwork(f)
  1577. node := getMockNode(f)
  1578. lb := getMockLoadBalancer(f)
  1579. sa := getMockSharedAsset(f)
  1580. assets := map[string]Asset{"any": &any, "cloud": &cloud}
  1581. as := AssetSet{
  1582. Assets: assets,
  1583. Any: map[string]*Any{"NaN": &any},
  1584. Cloud: map[string]*Cloud{"NaN": &cloud},
  1585. ClusterManagement: map[string]*ClusterManagement{"NaN": &cm},
  1586. Disks: map[string]*Disk{"NaN": &disk},
  1587. Network: map[string]*Network{"NaN": &network},
  1588. Nodes: map[string]*Node{"NaN": &node},
  1589. LoadBalancers: map[string]*LoadBalancer{"NaN": &lb},
  1590. SharedAssets: map[string]*SharedAsset{"NaN": &sa},
  1591. }
  1592. return as
  1593. }