provider_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. package azure
  2. import (
  3. "fmt"
  4. "testing"
  5. "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute"
  6. "github.com/Azure/azure-sdk-for-go/services/preview/commerce/mgmt/2015-06-01-preview/commerce"
  7. "github.com/stretchr/testify/require"
  8. "github.com/opencost/opencost/core/pkg/util/mathutil"
  9. "github.com/opencost/opencost/pkg/cloud/models"
  10. )
  11. func TestParseAzureSubscriptionID(t *testing.T) {
  12. cases := []struct {
  13. input string
  14. expected string
  15. }{
  16. {
  17. input: "azure:///subscriptions/0badafdf-1234-abcd-wxyz-123456789/...",
  18. expected: "0badafdf-1234-abcd-wxyz-123456789",
  19. },
  20. {
  21. input: "azure:/subscriptions/0badafdf-1234-abcd-wxyz-123456789/...",
  22. expected: "",
  23. },
  24. {
  25. input: "azure:///subscriptions//",
  26. expected: "",
  27. },
  28. {
  29. input: "",
  30. expected: "",
  31. },
  32. }
  33. for _, test := range cases {
  34. result := ParseAzureSubscriptionID(test.input)
  35. if result != test.expected {
  36. t.Errorf("Input: %s, Expected: %s, Actual: %s", test.input, test.expected, result)
  37. }
  38. }
  39. }
  40. func TestConvertMeterToPricings(t *testing.T) {
  41. regions := map[string]string{
  42. "useast": "US East",
  43. "japanwest": "Japan West",
  44. "australiasoutheast": "Australia Southeast",
  45. "norwaywest": "Norway West",
  46. }
  47. baseCPUPrice := "0.30000"
  48. meterInfo := func(category, subcategory, name, region string, rate float64) commerce.MeterInfo {
  49. return commerce.MeterInfo{
  50. MeterCategory: &category,
  51. MeterSubCategory: &subcategory,
  52. MeterName: &name,
  53. MeterRegion: &region,
  54. MeterRates: map[string]*float64{"0": &rate},
  55. }
  56. }
  57. t.Run("windows", func(t *testing.T) {
  58. info := meterInfo("Virtual Machines", "D2 Series Windows", "D2s v3", "AU Southeast", 0.3)
  59. results, err := convertMeterToPricings(info, regions, baseCPUPrice)
  60. require.NoError(t, err)
  61. require.Nil(t, results)
  62. })
  63. t.Run("storage", func(t *testing.T) {
  64. info := meterInfo("Storage", "Some SSD type", "P4 are good", "US East", 2000)
  65. results, err := convertMeterToPricings(info, regions, baseCPUPrice)
  66. require.NoError(t, err)
  67. expected := map[string]*AzurePricing{
  68. "useast,premium_ssd": {
  69. PV: &models.PV{Cost: "0.085616", Region: "useast"},
  70. },
  71. }
  72. require.Equal(t, expected, results)
  73. })
  74. t.Run("virtual machines", func(t *testing.T) {
  75. info := meterInfo("Virtual Machines", "Eav4/Easv4 Series", "E96a v4/E96as v4 Low Priority", "JA West", 10)
  76. results, err := convertMeterToPricings(info, regions, baseCPUPrice)
  77. require.NoError(t, err)
  78. expected := map[string]*AzurePricing{
  79. "japanwest,Standard_E96a_v4,preemptible": {
  80. Node: &models.Node{Cost: "10.000000", BaseCPUPrice: "0.30000", UsageType: "preemptible"},
  81. },
  82. "japanwest,Standard_E96as_v4,preemptible": {
  83. Node: &models.Node{Cost: "10.000000", BaseCPUPrice: "0.30000", UsageType: "preemptible"},
  84. },
  85. }
  86. require.Equal(t, expected, results)
  87. })
  88. }
  89. func TestAzure_findCostForDisk(t *testing.T) {
  90. var loc string = "location"
  91. var size int32 = 1
  92. az := &Azure{
  93. Pricing: map[string]*AzurePricing{
  94. "location,nil": nil,
  95. "location,nilpv": {
  96. PV: nil,
  97. },
  98. "location,ssd": {
  99. PV: &models.PV{
  100. Cost: "1",
  101. },
  102. },
  103. },
  104. }
  105. testCases := []struct {
  106. name string
  107. disk *compute.Disk
  108. exp float64
  109. expErr error
  110. }{
  111. {
  112. "disk is nil",
  113. nil,
  114. 0.0,
  115. fmt.Errorf("disk is empty"),
  116. },
  117. {
  118. "nil location",
  119. &compute.Disk{
  120. Location: nil,
  121. Sku: &compute.DiskSku{
  122. Name: "ssd",
  123. },
  124. DiskProperties: &compute.DiskProperties{
  125. DiskSizeGB: &size,
  126. },
  127. },
  128. 0.0,
  129. fmt.Errorf("failed to find pricing for key: ,ssd"),
  130. },
  131. {
  132. "nil disk properties",
  133. &compute.Disk{
  134. Location: &loc,
  135. Sku: &compute.DiskSku{
  136. Name: "ssd",
  137. },
  138. DiskProperties: nil,
  139. },
  140. 0.0,
  141. fmt.Errorf("disk properties are nil"),
  142. },
  143. {
  144. "nil disk size",
  145. &compute.Disk{
  146. Location: &loc,
  147. Sku: &compute.DiskSku{
  148. Name: "ssd",
  149. },
  150. DiskProperties: &compute.DiskProperties{
  151. DiskSizeGB: nil,
  152. },
  153. },
  154. 0.0,
  155. fmt.Errorf("disk size is nil"),
  156. },
  157. {
  158. "sku does not exist",
  159. &compute.Disk{
  160. Location: &loc,
  161. Sku: &compute.DiskSku{
  162. Name: "doesnotexist",
  163. },
  164. DiskProperties: &compute.DiskProperties{
  165. DiskSizeGB: &size,
  166. },
  167. },
  168. 0.0,
  169. fmt.Errorf("failed to find pricing for key: location,doesnotexist"),
  170. },
  171. {
  172. "pricing is nil",
  173. &compute.Disk{
  174. Sku: &compute.DiskSku{
  175. Name: "nil",
  176. },
  177. DiskProperties: &compute.DiskProperties{
  178. DiskSizeGB: &size,
  179. },
  180. },
  181. 0.0,
  182. fmt.Errorf("failed to find pricing for key: location,nil"),
  183. },
  184. {
  185. "pricing.PV is nil",
  186. &compute.Disk{
  187. Sku: &compute.DiskSku{
  188. Name: "nilpv",
  189. },
  190. DiskProperties: &compute.DiskProperties{
  191. DiskSizeGB: &size,
  192. },
  193. },
  194. 0.0,
  195. fmt.Errorf("pricing for key 'location,nilpv' has nil PV"),
  196. },
  197. {
  198. "valid (ssd)",
  199. &compute.Disk{
  200. Location: &loc,
  201. Sku: &compute.DiskSku{
  202. Name: "ssd",
  203. },
  204. DiskProperties: &compute.DiskProperties{
  205. DiskSizeGB: &size,
  206. },
  207. },
  208. 730.0,
  209. nil,
  210. },
  211. }
  212. for _, tc := range testCases {
  213. t.Run(tc.name, func(t *testing.T) {
  214. act, actErr := az.findCostForDisk(tc.disk)
  215. if actErr != nil && tc.expErr == nil {
  216. t.Fatalf("unexpected error: %s", actErr)
  217. }
  218. if tc.expErr != nil && actErr == nil {
  219. t.Fatalf("missing expected error: %s", tc.expErr)
  220. }
  221. if !mathutil.Approximately(tc.exp, act) {
  222. t.Fatalf("expected value %f; got %f", tc.exp, act)
  223. }
  224. })
  225. }
  226. }