| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687 |
- package basic
- import (
- "context"
- "os"
- "testing"
- "github.com/opencost/opencost/core/pkg/pricing"
- "github.com/opencost/opencost/core/pkg/reader"
- "github.com/opencost/opencost/core/pkg/storage"
- "github.com/opencost/opencost/core/pkg/unit"
- "github.com/stretchr/testify/require"
- )
- // TestNewBasicPricingModuleEmptyStore verifies the constructor populates a
- // default pricing set when given an empty store.
- func TestNewBasicPricingModuleEmptyStore(t *testing.T) {
- store := pricing.NewMemoryPricingStore()
- pm, err := NewBasicPricingModule(store)
- require.NoError(t, err)
- ps, err := store.GetPricingSet(t.Context())
- require.NoError(t, err)
- require.False(t, ps.IsEmpty())
- np, err := pm.getNodePricing(t.Context())
- require.NoError(t, err)
- require.NotNil(t, np)
- }
- func TestPricingModule(t *testing.T) {
- memoryPricingStore := pricing.NewMemoryPricingStore()
- filePricingStore, err := pricing.NewStoragePricingStore(t.Context(), newFileStorage(t), "pricing.json")
- require.NoError(t, err)
- stores := map[string]pricing.PricingStore{
- "MemoryPricingStore": memoryPricingStore,
- "StoragePricingStore": filePricingStore,
- }
- for name, store := range stores {
- t.Run(name, testPricingModuleWithStore(store))
- }
- }
- func testPricingModuleWithStore(store pricing.PricingStore) func(t *testing.T) {
- return func(t *testing.T) {
- ctx := t.Context()
- pm, err := NewBasicPricingModule(store)
- require.NoError(t, err)
- t.Run("DefaultPricing", func(t *testing.T) {
- testDefaultPricing(t, ctx, pm)
- })
- t.Run("Metadata", func(t *testing.T) {
- testMetadata(t, pm)
- })
- t.Run("GetPricingSet", func(t *testing.T) {
- testGetPricingSet(t, ctx, pm)
- })
- t.Run("PublicGetters", func(t *testing.T) {
- testPublicGetters(t, ctx, pm)
- })
- t.Run("SetClusterPricePerHour", func(t *testing.T) {
- testSetClusterPricePerHour(t, ctx, pm)
- })
- t.Run("SetNetworkPrices", func(t *testing.T) {
- testSetNetworkPrices(t, ctx, pm)
- })
- t.Run("SetServicePricePerHour", func(t *testing.T) {
- testSetServicePricePerHour(t, ctx, pm)
- })
- t.Run("Checksum", func(t *testing.T) {
- testChecksum(t, ctx, pm)
- })
- t.Run("SetNodePricePerCPUCoreHour", func(t *testing.T) {
- testSetNodePricePerCPUCoreHour(t, ctx, pm)
- })
- t.Run("SetNodePricePerRAMGiBHour", func(t *testing.T) {
- testSetNodePricePerRAMGiBHour(t, ctx, pm)
- })
- t.Run("SetNodePricePerGPUHour", func(t *testing.T) {
- testSetNodePricePerGPUHour(t, ctx, pm)
- })
- t.Run("SetNodePricePerLocalDiskGiBHour", func(t *testing.T) {
- testSetNodePricePerLocalDiskGiBHour(t, ctx, pm)
- })
- t.Run("SetVolumePricePerStorageGiBHour", func(t *testing.T) {
- testSetVolumePricePerStorageGiBHour(t, ctx, pm)
- })
- t.Run("NewNodePricingReader", func(t *testing.T) {
- testNewNodePricingReader(t, ctx, pm)
- })
- t.Run("NewVolumePricingReader", func(t *testing.T) {
- testNewVolumePricingReader(t, ctx, pm)
- })
- t.Run("NewClusterPricingReader", func(t *testing.T) {
- testNewClusterPricingReader(t, ctx, pm)
- })
- t.Run("NewNetworkPricingReader", func(t *testing.T) {
- testNewNetworkPricingReader(t, ctx, pm)
- })
- t.Run("NewServicePricingReader", func(t *testing.T) {
- testNewServicePricingReader(t, ctx, pm)
- })
- t.Run("ModulePersistence", func(t *testing.T) {
- // Create a new PricingModule with the same store
- pm2, err := NewBasicPricingModule(store)
- require.NoError(t, err)
- // Verify that pricing persists across all resource kinds.
- np, err := pm2.getNodePricing(ctx)
- require.NoError(t, err, "getting node pricing")
- require.NotNil(t, np, "expected node pricing to be persisted")
- cp, err := pm2.getClusterPricing(ctx)
- require.NoError(t, err, "getting cluster pricing")
- require.NotNil(t, cp, "expected cluster pricing to be persisted")
- netp, err := pm2.getNetworkPricing(ctx)
- require.NoError(t, err, "getting network pricing")
- require.NotNil(t, netp, "expected network pricing to be persisted")
- vp, err := pm2.getPersistentVolumePricing(ctx)
- require.NoError(t, err, "getting volume pricing")
- require.NotNil(t, vp, "expected volume pricing to be persisted")
- sp, err := pm2.getServicePricing(ctx)
- require.NoError(t, err, "getting service pricing")
- require.NotNil(t, sp, "expected service pricing to be persisted")
- })
- }
- }
- // testDefaultPricing verifies that a freshly created PricingModule contains default pricing
- func testDefaultPricing(t *testing.T, ctx context.Context, pm *PricingModule) {
- // Test default node pricing
- np, err := pm.getNodePricing(ctx)
- if err != nil {
- t.Fatalf("Failed to get node pricing: %v", err)
- }
- if np == nil {
- t.Fatal("Expected node pricing to exist")
- }
- // Prices are keyed by Resource. RAM and local disk share the GiB-hr unit,
- // so the Resource key is what distinguishes them.
- nodeChecks := []struct {
- resource pricing.Resource
- want float64
- }{
- {pricing.ResourceCPU, DefaultNodePricePerVCPUHour},
- {pricing.ResourceRAM, DefaultNodePricePerRAMGiBHour},
- {pricing.ResourceGPU, DefaultNodePricePerGPUHour},
- {pricing.ResourceStorage, DefaultNodePricePerLocalDiskGiBHour},
- }
- for _, c := range nodeChecks {
- price, ok := np.Prices[c.resource]
- if !ok {
- t.Errorf("Expected to find %s pricing", c.resource)
- continue
- }
- if price.Price != c.want {
- t.Errorf("Expected %s price to be %f, got %f", c.resource, c.want, price.Price)
- }
- }
- // Test default volume pricing
- vp, err := pm.getPersistentVolumePricing(ctx)
- if err != nil {
- t.Fatalf("Failed to get volume pricing: %v", err)
- }
- if vp == nil {
- t.Fatal("Expected volume pricing to exist")
- }
- volumePrice, ok := vp.Prices[pricing.ResourceStorage]
- if !ok {
- t.Fatal("Expected to find volume storage pricing")
- }
- if volumePrice.Price != DefaultPersistentVolumePricePerGiBHour {
- t.Errorf("Expected volume price to be %f, got %f", DefaultPersistentVolumePricePerGiBHour, volumePrice.Price)
- }
- // Test default cluster pricing
- cp, err := pm.getClusterPricing(ctx)
- require.NoError(t, err, "getting cluster pricing")
- require.NotNil(t, cp, "expected cluster pricing to exist")
- clusterPrice, ok := cp.Prices[pricing.ResourceCluster]
- require.True(t, ok, "expected to find cluster pricing")
- require.Equal(t, DefaultClusterPricePerHour, clusterPrice.Price)
- require.Equal(t, unit.Hour, clusterPrice.Unit)
- // Test default network pricing. RAM, egress types, etc. all share the GiB
- // unit, so the Resource key distinguishes them.
- netp, err := pm.getNetworkPricing(ctx)
- require.NoError(t, err, "getting network pricing")
- require.NotNil(t, netp, "expected network pricing to exist")
- networkChecks := []struct {
- resource pricing.Resource
- want float64
- }{
- {pricing.ResourceLocalEgress, DefaultNetworkLocalEgressPricePerGiB},
- {pricing.ResourceCrossZoneEgress, DefaultNetworkCrossZoneEgressPricePerGiB},
- {pricing.ResourceCrossRegionEgress, DefaultNetworkCrossRegionEgressPricePerGiB},
- {pricing.ResourceInternetEgress, DefaultNetworkInternetEgressPricePerGiB},
- {pricing.ResourceNATGatewayEgress, DefaultNetworkNATGatewayEgressPricePerGiB},
- {pricing.ResourceNATGatewayIngress, DefaultNetworkNATGatewayIngressPricePerGiB},
- }
- for _, c := range networkChecks {
- price, ok := netp.Prices[c.resource]
- require.Truef(t, ok, "expected to find %s pricing", c.resource)
- require.Equalf(t, c.want, price.Price, "price for %s", c.resource)
- require.Equalf(t, unit.GiB, price.Unit, "unit for %s", c.resource)
- }
- // Test default service pricing
- sp, err := pm.getServicePricing(ctx)
- require.NoError(t, err, "getting service pricing")
- require.NotNil(t, sp, "expected service pricing to exist")
- servicePrice, ok := sp.Prices[pricing.ResourceService]
- require.True(t, ok, "expected to find service pricing")
- require.Equal(t, DefaultServicePricePerHour, servicePrice.Price)
- require.Equal(t, unit.Hour, servicePrice.Unit)
- }
- // testSetNodePricePerCPUCoreHour tests the SetNodePricePerCPUCoreHour function
- func testSetNodePricePerCPUCoreHour(t *testing.T, ctx context.Context, pm *PricingModule) {
- newPrice := 0.075
- err := pm.SetNodePricePerCPUCoreHour(ctx, newPrice)
- if err != nil {
- t.Fatalf("Failed to set CPU price: %v", err)
- }
- // Verify the price was set
- np, err := pm.getNodePricing(ctx)
- if err != nil {
- t.Fatalf("Failed to get node pricing: %v", err)
- }
- price, ok := np.Prices[pricing.ResourceCPU]
- if !ok {
- t.Fatal("Expected to find CPU pricing")
- }
- if price.Price != newPrice {
- t.Errorf("Expected CPU price to be %f, got %f", newPrice, price.Price)
- }
- }
- // testSetNodePricePerRAMGiBHour tests the SetNodePricePerRAMGiBHour function
- func testSetNodePricePerRAMGiBHour(t *testing.T, ctx context.Context, pm *PricingModule) {
- newPrice := 0.008
- err := pm.SetNodePricePerRAMGiBHour(ctx, newPrice)
- if err != nil {
- t.Fatalf("Failed to set RAM price: %v", err)
- }
- // Verify the price was set
- np, err := pm.getNodePricing(ctx)
- if err != nil {
- t.Fatalf("Failed to get node pricing: %v", err)
- }
- price, ok := np.Prices[pricing.ResourceRAM]
- if !ok {
- t.Fatal("Expected to find RAM pricing")
- }
- if price.Price != newPrice {
- t.Errorf("Expected RAM price to be %f, got %f", newPrice, price.Price)
- }
- }
- // testSetNodePricePerGPUHour tests the SetNodePricePerGPUHour function
- func testSetNodePricePerGPUHour(t *testing.T, ctx context.Context, pm *PricingModule) {
- newPrice := 2.0
- err := pm.SetNodePricePerGPUHour(ctx, newPrice)
- if err != nil {
- t.Fatalf("Failed to set GPU price: %v", err)
- }
- // Verify the price was set
- np, err := pm.getNodePricing(ctx)
- if err != nil {
- t.Fatalf("Failed to get node pricing: %v", err)
- }
- price, ok := np.Prices[pricing.ResourceGPU]
- if !ok {
- t.Fatal("Expected to find GPU pricing")
- }
- if price.Price != newPrice {
- t.Errorf("Expected GPU price to be %f, got %f", newPrice, price.Price)
- }
- }
- // testSetNodePricePerLocalDiskGiBHour tests the SetNodePricePerLocalDiskGiBHour function
- func testSetNodePricePerLocalDiskGiBHour(t *testing.T, ctx context.Context, pm *PricingModule) {
- newPrice := 0.0007
- err := pm.SetNodePricePerLocalDiskGiBHour(ctx, newPrice)
- if err != nil {
- t.Fatalf("Failed to set local disk price: %v", err)
- }
- // Verify the price was set
- np, err := pm.getNodePricing(ctx)
- if err != nil {
- t.Fatalf("Failed to get node pricing: %v", err)
- }
- price, ok := np.Prices[pricing.ResourceStorage]
- if !ok {
- t.Fatal("Expected to find local disk pricing")
- }
- if price.Price != newPrice {
- t.Errorf("Expected local disk price to be %f, got %f", newPrice, price.Price)
- }
- }
- // testSetVolumePricePerStorageGiBHour tests the SetVolumePricePerStorageGiBHour function
- func testSetVolumePricePerStorageGiBHour(t *testing.T, ctx context.Context, pm *PricingModule) {
- newPrice := 0.0003
- err := pm.SetPersistentVolumePricePerStorageGiBHour(ctx, newPrice)
- if err != nil {
- t.Fatalf("Failed to set volume storage price: %v", err)
- }
- // Verify the price was set
- vp, err := pm.getPersistentVolumePricing(ctx)
- if err != nil {
- t.Fatalf("Failed to get volume pricing: %v", err)
- }
- price, ok := vp.Prices[pricing.ResourceStorage]
- if !ok {
- t.Fatal("Expected to find volume storage pricing")
- }
- if price.Price != newPrice {
- t.Errorf("Expected volume storage price to be %f, got %f", newPrice, price.Price)
- }
- }
- // testNewNodePricingReader tests the NewNodePricingReader function
- func testNewNodePricingReader(t *testing.T, ctx context.Context, pm *PricingModule) {
- // Test that NewNodePricingReader always produces a reader
- rdr, err := pm.NewNodePricingReader(ctx)
- if err != nil {
- t.Fatalf("Failed to create node pricing reader: %v", err)
- }
- if rdr == nil {
- t.Fatal("Expected reader to be non-nil")
- }
- // Test that the reader produces precisely one *NodePricing struct
- dst := make([]*pricing.NodePricing, 10) // Buffer larger than expected
- count := 0
- for {
- n, err := rdr.Read(ctx, dst)
- count += n
- // Verify all read items are non-nil
- for i := 0; i < n; i++ {
- if dst[i] == nil {
- t.Error("Expected non-nil NodePricing")
- }
- }
- if err == reader.Done {
- break
- }
- if err != nil {
- t.Fatalf("Reader error: %v", err)
- }
- }
- if count != 1 {
- t.Errorf("Expected reader to produce exactly 1 NodePricing, got %d", count)
- }
- // Clean up
- if err := rdr.Close(); err != nil {
- t.Errorf("Failed to close reader: %v", err)
- }
- }
- // testNewVolumePricingReader tests the NewVolumePricingReader function
- func testNewVolumePricingReader(t *testing.T, ctx context.Context, pm *PricingModule) {
- // Test that NewVolumePricingReader always produces a reader
- rdr, err := pm.NewPersistentVolumePricingReader(ctx)
- if err != nil {
- t.Fatalf("Failed to create volume pricing reader: %v", err)
- }
- if rdr == nil {
- t.Fatal("Expected reader to be non-nil")
- }
- // Test that the reader produces precisely one *VolumePricing struct
- dst := make([]*pricing.PersistentVolumePricing, 10) // Buffer larger than expected
- count := 0
- for {
- n, err := rdr.Read(ctx, dst)
- count += n
- // Verify all read items are non-nil
- for i := 0; i < n; i++ {
- if dst[i] == nil {
- t.Error("Expected non-nil VolumePricing")
- }
- }
- if err == reader.Done {
- break
- }
- if err != nil {
- t.Fatalf("Reader error: %v", err)
- }
- }
- if count != 1 {
- t.Errorf("Expected reader to produce exactly 1 VolumePricing, got %d", count)
- }
- // Clean up
- if err := rdr.Close(); err != nil {
- t.Errorf("Failed to close reader: %v", err)
- }
- }
- // testMetadata verifies the source identity accessors.
- func testMetadata(t *testing.T, pm *PricingModule) {
- require.Equal(t, SourceKind, pm.SourceKind())
- require.Equal(t, SourceName, pm.SourceName())
- }
- // testGetPricingSet verifies GetPricingSet returns a populated set.
- func testGetPricingSet(t *testing.T, ctx context.Context, pm *PricingModule) {
- ps, err := pm.GetPricingSet(ctx)
- require.NoError(t, err)
- require.NotNil(t, ps)
- require.False(t, ps.IsEmpty())
- }
- // testPublicGetters exercises the public Get*Pricing wrappers, which apply a
- // nil-check around the private getters.
- func testPublicGetters(t *testing.T, ctx context.Context, pm *PricingModule) {
- cp, err := pm.GetClusterPricing(ctx, pricing.ClusterPricingProperties{})
- require.NoError(t, err)
- require.NotNil(t, cp)
- netp, err := pm.GetNetworkPricing(ctx, pricing.NetworkPricingProperties{})
- require.NoError(t, err)
- require.NotNil(t, netp)
- np, err := pm.GetNodePricing(ctx, pricing.NodePricingProperties{})
- require.NoError(t, err)
- require.NotNil(t, np)
- vp, err := pm.GetPersistentVolumePricing(ctx, pricing.PersistentVolumePricingProperties{})
- require.NoError(t, err)
- require.NotNil(t, vp)
- sp, err := pm.GetServicePricing(ctx, pricing.ServicePricingProperties{})
- require.NoError(t, err)
- require.NotNil(t, sp)
- }
- // testSetClusterPricePerHour tests the SetClusterPricePerHour function.
- func testSetClusterPricePerHour(t *testing.T, ctx context.Context, pm *PricingModule) {
- newPrice := 1.25
- err := pm.SetClusterPricePerHour(ctx, newPrice)
- require.NoError(t, err)
- cp, err := pm.getClusterPricing(ctx)
- require.NoError(t, err)
- price, ok := cp.Prices[pricing.ResourceCluster]
- require.True(t, ok, "expected to find cluster pricing")
- require.Equal(t, newPrice, price.Price)
- require.Equal(t, unit.Hour, price.Unit)
- }
- // testSetNetworkPrices tests each network setter. Each case verifies that only
- // the targeted Resource changes and that the other network prices are left
- // intact, guarding against a setter writing the wrong Resource key.
- func testSetNetworkPrices(t *testing.T, ctx context.Context, pm *PricingModule) {
- cases := []struct {
- name string
- resource pricing.Resource
- newPrice float64
- set func(context.Context, float64) error
- }{
- {"LocalEgress", pricing.ResourceLocalEgress, 0.002, pm.SetNetworkLocalEgressPricePerGiB},
- {"CrossZoneEgress", pricing.ResourceCrossZoneEgress, 0.012, pm.SetNetworkCrossZoneEgressPricePerGiB},
- {"CrossRegionEgress", pricing.ResourceCrossRegionEgress, 0.022, pm.SetNetworkCrossRegionEgressPricePerGiB},
- {"InternetEgress", pricing.ResourceInternetEgress, 0.111, pm.SetNetworkInternetEgressPricePerGiB},
- {"NATGatewayEgress", pricing.ResourceNATGatewayEgress, 0.055, pm.SetNetworkNATGatewayEgressPricePerGiB},
- {"NATGatewayIngress", pricing.ResourceNATGatewayIngress, 0.066, pm.SetNetworkNATGatewayIngressPricePerGiB},
- }
- for _, c := range cases {
- t.Run(c.name, func(t *testing.T) {
- // Snapshot existing prices so we can verify non-targeted
- // resources are untouched.
- before, err := pm.getNetworkPricing(ctx)
- require.NoError(t, err)
- prev := make(map[pricing.Resource]float64, len(before.Prices))
- for r, p := range before.Prices {
- prev[r] = p.Price
- }
- require.NoError(t, c.set(ctx, c.newPrice))
- after, err := pm.getNetworkPricing(ctx)
- require.NoError(t, err)
- price, ok := after.Prices[c.resource]
- require.Truef(t, ok, "expected to find %s pricing", c.resource)
- require.Equal(t, c.newPrice, price.Price)
- require.Equal(t, unit.GiB, price.Unit)
- // All other network resources must be unchanged.
- for r, want := range prev {
- if r == c.resource {
- continue
- }
- require.Equalf(t, want, after.Prices[r].Price, "unexpected change to %s", r)
- }
- })
- }
- }
- // testSetServicePricePerHour tests the SetServicePricePerHour function.
- func testSetServicePricePerHour(t *testing.T, ctx context.Context, pm *PricingModule) {
- newPrice := 0.05
- err := pm.SetServicePricePerHour(ctx, newPrice)
- require.NoError(t, err)
- sp, err := pm.getServicePricing(ctx)
- require.NoError(t, err)
- price, ok := sp.Prices[pricing.ResourceService]
- require.True(t, ok, "expected to find service pricing")
- require.Equal(t, newPrice, price.Price)
- require.Equal(t, unit.Hour, price.Unit)
- }
- // testChecksum verifies the checksum is non-empty, deterministic, and sensitive
- // to pricing mutations.
- func testChecksum(t *testing.T, ctx context.Context, pm *PricingModule) {
- sum1, err := pm.Checksum(ctx)
- require.NoError(t, err)
- require.NotEmpty(t, sum1)
- // Deterministic: recomputing without mutation yields the same checksum.
- sum2, err := pm.Checksum(ctx)
- require.NoError(t, err)
- require.Equal(t, sum1, sum2)
- // Sensitive: mutating pricing changes the checksum.
- require.NoError(t, pm.SetServicePricePerHour(ctx, sum1Sentinel))
- sum3, err := pm.Checksum(ctx)
- require.NoError(t, err)
- require.NotEqual(t, sum1, sum3, "expected checksum to change after a price mutation")
- }
- // sum1Sentinel is an arbitrary price unlikely to match the existing service
- // price, used to force a checksum change.
- const sum1Sentinel = 0.0419
- // testNewClusterPricingReader verifies the cluster pricing reader yields exactly
- // one non-nil element.
- func testNewClusterPricingReader(t *testing.T, ctx context.Context, pm *PricingModule) {
- rdr, err := pm.NewClusterPricingReader(ctx)
- require.NoError(t, err)
- require.NotNil(t, rdr)
- dst := make([]*pricing.ClusterPricing, 10)
- count := 0
- for {
- n, err := rdr.Read(ctx, dst)
- count += n
- for i := 0; i < n; i++ {
- require.NotNil(t, dst[i], "expected non-nil ClusterPricing")
- }
- if err == reader.Done {
- break
- }
- require.NoError(t, err)
- }
- require.Equal(t, 1, count, "expected exactly 1 ClusterPricing")
- require.NoError(t, rdr.Close())
- }
- // testNewNetworkPricingReader verifies the network pricing reader yields exactly
- // one non-nil element.
- func testNewNetworkPricingReader(t *testing.T, ctx context.Context, pm *PricingModule) {
- rdr, err := pm.NewNetworkPricingReader(ctx)
- require.NoError(t, err)
- require.NotNil(t, rdr)
- dst := make([]*pricing.NetworkPricing, 10)
- count := 0
- for {
- n, err := rdr.Read(ctx, dst)
- count += n
- for i := 0; i < n; i++ {
- require.NotNil(t, dst[i], "expected non-nil NetworkPricing")
- }
- if err == reader.Done {
- break
- }
- require.NoError(t, err)
- }
- require.Equal(t, 1, count, "expected exactly 1 NetworkPricing")
- require.NoError(t, rdr.Close())
- }
- // testNewServicePricingReader verifies the service pricing reader yields exactly
- // one non-nil element.
- func testNewServicePricingReader(t *testing.T, ctx context.Context, pm *PricingModule) {
- rdr, err := pm.NewServicePricingReader(ctx)
- require.NoError(t, err)
- require.NotNil(t, rdr)
- dst := make([]*pricing.ServicePricing, 10)
- count := 0
- for {
- n, err := rdr.Read(ctx, dst)
- count += n
- for i := 0; i < n; i++ {
- require.NotNil(t, dst[i], "expected non-nil ServicePricing")
- }
- if err == reader.Done {
- break
- }
- require.NoError(t, err)
- }
- require.Equal(t, 1, count, "expected exactly 1 ServicePricing")
- require.NoError(t, rdr.Close())
- }
- func newFileStorage(t *testing.T) storage.Storage {
- tempDir, err := os.MkdirTemp("", "pricing-test-*")
- if err != nil {
- t.Fatalf("Failed to create temp directory: %v", err)
- }
- defer os.RemoveAll(tempDir)
- return storage.NewFileStorage(tempDir)
- }
|