| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- package azure
- import (
- "context"
- "fmt"
- "strings"
- "testing"
- "github.com/Azure/azure-sdk-for-go/profiles/2020-09-01/commerce/mgmt/commerce"
- "github.com/opencost/opencost/pkg/cloud/models"
- "github.com/stretchr/testify/require"
- )
- func TestDownloader(t *testing.T) {
- d := PriceSheetDownloader{
- TenantID: "test-tenant-id",
- ClientID: "test-client-id",
- ClientSecret: "test-client-secret",
- BillingAccount: "test-billing-account",
- OfferID: "my-offer-id",
- ConvertMeterInfo: convertMeter,
- }
- t.Run("read prices", func(t *testing.T) {
- results, err := d.readPricesheet(context.Background(), strings.NewReader(pricesheetData))
- require.NoError(t, err)
- // Units and prices are normalised.
- // Info for saving plans and other offers is skipped.
- expected := map[string]*AzurePricing{
- "DC96as_v4 1 Hour": {Node: &models.Node{Cost: "10.505"}},
- "DC2as_v4 1 Hour": {Node: &models.Node{Cost: "0.219"}},
- "VM1 1 Hour": {Node: &models.Node{Cost: "1.0"}},
- "VM2 1 Hour": {Node: &models.Node{Cost: "2.0"}},
- }
- require.Equal(t, expected, results)
- })
- t.Run("bad header", func(t *testing.T) {
- data := "\n\nMeter ID,Meter name,Meter category,Something else,,,,,,,,,,,,,,\n"
- _, err := d.readPricesheet(context.Background(), strings.NewReader(data))
- require.ErrorContains(t, err, `unexpected header at col 3 "Something else", expected "Meter sub-category"`)
- })
- t.Run("short header", func(t *testing.T) {
- data := "\n\nMeter ID, Meter name, Meter category, Meter sub-category\n"
- _, err := d.readPricesheet(context.Background(), strings.NewReader(data))
- require.ErrorContains(t, err, "too few header columns: got 4, expected 14")
- })
- t.Run("no matching prices", func(t *testing.T) {
- d := PriceSheetDownloader{
- TenantID: "test-tenant-id",
- ClientID: "test-client-id",
- ClientSecret: "test-client-secret",
- BillingAccount: "test-billing-account",
- OfferID: "my-offer-id",
- ConvertMeterInfo: func(commerce.MeterInfo) (map[string]*AzurePricing, error) {
- return nil, nil
- },
- }
- _, err := d.readPricesheet(context.Background(), strings.NewReader(pricesheetData))
- require.ErrorContains(t, err, "no matching pricing from price sheet")
- })
- }
- func convertMeter(info commerce.MeterInfo) (map[string]*AzurePricing, error) {
- switch *info.MeterName {
- case "skip-this":
- return nil, nil
- case "multiple-prices":
- return map[string]*AzurePricing{
- "VM1 1 Hour": {Node: &models.Node{Cost: "1.0"}},
- "VM2 1 Hour": {Node: &models.Node{Cost: "2.0"}},
- }, nil
- case "error":
- return nil, fmt.Errorf("there was an error handling this row!")
- default:
- return map[string]*AzurePricing{
- *info.MeterName + " " + *info.Unit: {
- Node: &models.Node{Cost: fmt.Sprintf("%0.3f", *info.MeterRates["0"])},
- },
- }, nil
- }
- }
- const pricesheetData = `Price Sheet Report for billing period - 202304
- Meter ID,Meter name,Meter category,Meter sub-category,Meter region,Unit,Unit of measure,Part number,Unit price,Currency code,Included quantity,Offer Id,Term,Price type
- d4236f8f-3ba6-5a9a-8c6b-14556538c44c,DC96as_v4,Virtual Machines,DCasv4 Series,US East,10 Hours,10 Hours,AAF-70822,105.050000000000000,USD,0.00,my-offer-id,,Consumption
- d4236f8f-3ba6-5a9a-8c6b-14556538c44c,DC96as_v4,Virtual Machines,DCasv4 Series,US East,10 Hours,10 Hours,AAF-70831,60.890000000000000,USD,0.00,other-offer-id,,Consumption
- e47a2c4c-4dc4-55d5-a8d7-ec5b1dcc9c08,DC2as_v4,Virtual Machines,DCasv4 Series,US East,100 Hours,100 Hours,AAF-70890,21.900000000000000,USD,0.000,my-offer-id,,Consumption
- e47a2c4c-4dc4-55d5-a8d7-ec5b1dcc9c08,DC2as_v4,Virtual Machines,DCasv4 Series,US East,100 Hours,100 Hours,AAF-70886,12.700000000000000,USD,0.000,other-offer-id,,Consumption
- cb8d72c0-2b02-5b41-9ac9-2809c04f17ff,DC16as_v4,Virtual Machines,DCasv4 Series,US East,10 Hours,10 Hours,AAF-70911,17.510000000000000,USD,0.00,my-offer-id,,Savings Plan
- cb8d72c0-2b02-5b41-9ac9-2809c04f17ff,DC16as_v4,Virtual Machines,DCasv4 Series,US East,10 Hours,10 Hours,AAF-70910,10.150000000000000,USD,0.00,other-offer-id,,Consumption
- d4236f8f-3ba6-5a9a-8c6b-14556538c44c,skip-this,Virtual Machines,DCasv4 Series,US East,10 Hours,10 Hours,AAF-70822,105.050000000000000,USD,0.00,my-offer-id,,Consumption
- d4236f8f-3ba6-5a9a-8c6b-14556538c44c,multiple-prices,Virtual Machines,DCasv4 Series,US East,10 Hours,10 Hours,AAF-70822,105.050000000000000,USD,0.00,my-offer-id,,Consumption
- d4236f8f-3ba6-5a9a-8c6b-14556538c44c,error,Virtual Machines,DCasv4 Series,US East,10 Hours,10 Hours,AAF-70822,105.050000000000000,USD,0.00,my-offer-id,,Consumption
- `
|