draft tests
This commit is contained in:
		| @@ -151,9 +151,9 @@ func (r *PricedComputeResource) GetPrice() (float64, error) { | ||||
| 		if len(r.PricingProfiles) == 0 { | ||||
| 			return 0, errors.New("pricing profile must be set on Priced Compute" + r.ResourceID) | ||||
| 		} | ||||
| 		r.SelectedPricing = &r.PricingProfiles[0] | ||||
| 		r.SelectedPricing = r.PricingProfiles[0] | ||||
| 	} | ||||
| 	pricing := *r.SelectedPricing | ||||
| 	pricing := r.SelectedPricing | ||||
| 	price := float64(0) | ||||
| 	for _, l := range []map[string]float64{r.CPUsLocated, r.GPUsLocated} { | ||||
| 		for model, amountOfData := range l { | ||||
|   | ||||
| @@ -164,9 +164,9 @@ func (r *PricedDataResource) GetPrice() (float64, error) { | ||||
| 		if len(r.PricingProfiles) == 0 { | ||||
| 			return 0, errors.New("pricing profile must be set on Priced Data" + r.ResourceID) | ||||
| 		} | ||||
| 		r.SelectedPricing = &r.PricingProfiles[0] | ||||
| 		r.SelectedPricing = r.PricingProfiles[0] | ||||
| 	} | ||||
| 	pricing := *r.SelectedPricing | ||||
| 	pricing := r.SelectedPricing | ||||
| 	var err error | ||||
| 	amountOfData := float64(1) | ||||
| 	if pricing.GetOverrideStrategyValue() >= 0 { | ||||
|   | ||||
| @@ -14,7 +14,7 @@ type PricedResource struct { | ||||
| 	Logo                     string                      `json:"logo,omitempty" bson:"logo,omitempty"` | ||||
| 	InstancesRefs            map[string]string           `json:"instances_refs,omitempty" bson:"instances_refs,omitempty"` | ||||
| 	PricingProfiles          []pricing.PricingProfileITF `json:"pricing_profiles,omitempty" bson:"pricing_profiles,omitempty"` | ||||
| 	SelectedPricing          *pricing.PricingProfileITF  `json:"selected_pricing,omitempty" bson:"selected_pricing,omitempty"` | ||||
| 	SelectedPricing          pricing.PricingProfileITF   `json:"selected_pricing,omitempty" bson:"selected_pricing,omitempty"` | ||||
| 	ExplicitBookingDurationS float64                     `json:"explicit_location_duration_s,omitempty" bson:"explicit_location_duration_s,omitempty"` | ||||
| 	UsageStart               *time.Time                  `json:"start,omitempty" bson:"start,omitempty"` | ||||
| 	UsageEnd                 *time.Time                  `json:"end,omitempty" bson:"end,omitempty"` | ||||
| @@ -39,7 +39,7 @@ func (abs *PricedResource) IsPurchased() bool { | ||||
| 	if abs.SelectedPricing == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return (*abs.SelectedPricing).IsPurchased() | ||||
| 	return (abs.SelectedPricing).IsPurchased() | ||||
| } | ||||
|  | ||||
| func (abs *PricedResource) GetLocationEnd() *time.Time { | ||||
| @@ -86,8 +86,8 @@ func (r *PricedResource) GetPrice() (float64, error) { | ||||
| 		if len(r.PricingProfiles) == 0 { | ||||
| 			return 0, errors.New("pricing profile must be set on Priced Resource " + r.ResourceID) | ||||
| 		} | ||||
| 		r.SelectedPricing = &r.PricingProfiles[0] | ||||
| 		r.SelectedPricing = r.PricingProfiles[0] | ||||
| 	} | ||||
| 	pricing := *r.SelectedPricing | ||||
| 	pricing := r.SelectedPricing | ||||
| 	return pricing.GetPrice(1, 0, *r.UsageStart, *r.UsageEnd) | ||||
| } | ||||
|   | ||||
| @@ -9,13 +9,13 @@ import ( | ||||
| 	"cloud.o-forge.io/core/oc-lib/tools" | ||||
| ) | ||||
|  | ||||
| type purchaseResourceMongoAccessor struct { | ||||
| type PurchaseResourceMongoAccessor struct { | ||||
| 	utils.AbstractAccessor // AbstractAccessor contains the basic fields of an accessor (model, caller) | ||||
| } | ||||
|  | ||||
| // New creates a new instance of the bookingMongoAccessor | ||||
| func NewAccessor(request *tools.APIRequest) *purchaseResourceMongoAccessor { | ||||
| 	return &purchaseResourceMongoAccessor{ | ||||
| func NewAccessor(request *tools.APIRequest) *PurchaseResourceMongoAccessor { | ||||
| 	return &PurchaseResourceMongoAccessor{ | ||||
| 		AbstractAccessor: utils.AbstractAccessor{ | ||||
| 			Logger:  logs.CreateLogger(tools.PURCHASE_RESOURCE.String()), // Create a logger with the data type | ||||
| 			Request: request, | ||||
| @@ -27,23 +27,23 @@ func NewAccessor(request *tools.APIRequest) *purchaseResourceMongoAccessor { | ||||
| /* | ||||
| * Nothing special here, just the basic CRUD operations | ||||
|  */ | ||||
| func (a *purchaseResourceMongoAccessor) DeleteOne(id string) (utils.DBObject, int, error) { | ||||
| func (a *PurchaseResourceMongoAccessor) DeleteOne(id string) (utils.DBObject, int, error) { | ||||
| 	return utils.GenericDeleteOne(id, a) | ||||
| } | ||||
|  | ||||
| func (a *purchaseResourceMongoAccessor) UpdateOne(set utils.DBObject, id string) (utils.DBObject, int, error) { | ||||
| func (a *PurchaseResourceMongoAccessor) UpdateOne(set utils.DBObject, id string) (utils.DBObject, int, error) { | ||||
| 	return utils.GenericUpdateOne(set, id, a, &PurchaseResource{}) | ||||
| } | ||||
|  | ||||
| func (a *purchaseResourceMongoAccessor) StoreOne(data utils.DBObject) (utils.DBObject, int, error) { | ||||
| func (a *PurchaseResourceMongoAccessor) StoreOne(data utils.DBObject) (utils.DBObject, int, error) { | ||||
| 	return utils.GenericStoreOne(data, a) | ||||
| } | ||||
|  | ||||
| func (a *purchaseResourceMongoAccessor) CopyOne(data utils.DBObject) (utils.DBObject, int, error) { | ||||
| func (a *PurchaseResourceMongoAccessor) CopyOne(data utils.DBObject) (utils.DBObject, int, error) { | ||||
| 	return utils.GenericStoreOne(data, a) | ||||
| } | ||||
|  | ||||
| func (a *purchaseResourceMongoAccessor) LoadOne(id string) (utils.DBObject, int, error) { | ||||
| func (a *PurchaseResourceMongoAccessor) LoadOne(id string) (utils.DBObject, int, error) { | ||||
| 	return utils.GenericLoadOne[*PurchaseResource](id, func(d utils.DBObject) (utils.DBObject, int, error) { | ||||
| 		if d.(*PurchaseResource).EndDate != nil && time.Now().UTC().After(*d.(*PurchaseResource).EndDate) { | ||||
| 			utils.GenericDeleteOne(id, a) | ||||
| @@ -53,15 +53,15 @@ func (a *purchaseResourceMongoAccessor) LoadOne(id string) (utils.DBObject, int, | ||||
| 	}, a) | ||||
| } | ||||
|  | ||||
| func (a *purchaseResourceMongoAccessor) LoadAll(isDraft bool) ([]utils.ShallowDBObject, int, error) { | ||||
| func (a *PurchaseResourceMongoAccessor) LoadAll(isDraft bool) ([]utils.ShallowDBObject, int, error) { | ||||
| 	return utils.GenericLoadAll[*PurchaseResource](a.getExec(), isDraft, a) | ||||
| } | ||||
|  | ||||
| func (a *purchaseResourceMongoAccessor) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) { | ||||
| func (a *PurchaseResourceMongoAccessor) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) { | ||||
| 	return utils.GenericSearch[*PurchaseResource](filters, search, (&PurchaseResource{}).GetObjectFilters(search), a.getExec(), isDraft, a) | ||||
| } | ||||
|  | ||||
| func (a *purchaseResourceMongoAccessor) getExec() func(utils.DBObject) utils.ShallowDBObject { | ||||
| func (a *PurchaseResourceMongoAccessor) getExec() func(utils.DBObject) utils.ShallowDBObject { | ||||
| 	return func(d utils.DBObject) utils.ShallowDBObject { | ||||
| 		if d.(*PurchaseResource).EndDate != nil && time.Now().UTC().After(*d.(*PurchaseResource).EndDate) { | ||||
| 			utils.GenericDeleteOne(d.GetID(), a) | ||||
|   | ||||
| @@ -0,0 +1,56 @@ | ||||
| package purchase_resource_test | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
|  | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/purchase_resource" | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/utils" | ||||
| 	"cloud.o-forge.io/core/oc-lib/tools" | ||||
| ) | ||||
|  | ||||
| func TestGetAccessor(t *testing.T) { | ||||
| 	req := &tools.APIRequest{} | ||||
| 	res := &purchase_resource.PurchaseResource{} | ||||
| 	accessor := res.GetAccessor(req) | ||||
|  | ||||
| 	assert.NotNil(t, accessor) | ||||
| 	assert.Equal(t, tools.PURCHASE_RESOURCE, accessor.(*purchase_resource.PurchaseResourceMongoAccessor).Type) | ||||
| } | ||||
|  | ||||
| func TestCanUpdate(t *testing.T) { | ||||
| 	set := &purchase_resource.PurchaseResource{ResourceID: "id"} | ||||
| 	r := &purchase_resource.PurchaseResource{ | ||||
| 		AbstractObject: utils.AbstractObject{IsDraft: true}, | ||||
| 	} | ||||
| 	can, updated := r.CanUpdate(set) | ||||
| 	assert.True(t, can) | ||||
| 	assert.Equal(t, set, updated) | ||||
|  | ||||
| 	r.IsDraft = false | ||||
| 	can, _ = r.CanUpdate(set) | ||||
| 	assert.False(t, can) | ||||
| } | ||||
|  | ||||
| func TestCanDelete(t *testing.T) { | ||||
| 	now := time.Now().UTC() | ||||
| 	past := now.Add(-1 * time.Hour) | ||||
| 	future := now.Add(1 * time.Hour) | ||||
|  | ||||
| 	t.Run("nil EndDate", func(t *testing.T) { | ||||
| 		r := &purchase_resource.PurchaseResource{} | ||||
| 		assert.False(t, r.CanDelete()) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("EndDate in past", func(t *testing.T) { | ||||
| 		r := &purchase_resource.PurchaseResource{EndDate: &past} | ||||
| 		assert.True(t, r.CanDelete()) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("EndDate in future", func(t *testing.T) { | ||||
| 		r := &purchase_resource.PurchaseResource{EndDate: &future} | ||||
| 		assert.False(t, r.CanDelete()) | ||||
| 	}) | ||||
| } | ||||
| @@ -92,7 +92,7 @@ func (abs *AbstractInstanciatedResource[T]) SetAllowedInstances(request *tools.A | ||||
| 	if request != nil && request.PeerID == abs.CreatorID && request.PeerID != "" { | ||||
| 		return | ||||
| 	} | ||||
| 	abs.Instances = verifyAuthAction[T](abs.Instances, request) | ||||
| 	abs.Instances = VerifyAuthAction[T](abs.Instances, request) | ||||
| } | ||||
|  | ||||
| func (d *AbstractInstanciatedResource[T]) Trim() { | ||||
| @@ -105,10 +105,10 @@ func (d *AbstractInstanciatedResource[T]) Trim() { | ||||
| } | ||||
|  | ||||
| func (abs *AbstractInstanciatedResource[T]) VerifyAuth(request *tools.APIRequest) bool { | ||||
| 	return len(verifyAuthAction[T](abs.Instances, request)) > 0 || abs.AbstractObject.VerifyAuth(request) | ||||
| 	return len(VerifyAuthAction[T](abs.Instances, request)) > 0 || abs.AbstractObject.VerifyAuth(request) | ||||
| } | ||||
|  | ||||
| func verifyAuthAction[T ResourceInstanceITF](baseInstance []T, request *tools.APIRequest) []T { | ||||
| func VerifyAuthAction[T ResourceInstanceITF](baseInstance []T, request *tools.APIRequest) []T { | ||||
| 	instances := []T{} | ||||
| 	for _, instance := range baseInstance { | ||||
| 		_, peerGroups := instance.GetPeerGroups() | ||||
|   | ||||
| @@ -9,17 +9,17 @@ import ( | ||||
| 	"cloud.o-forge.io/core/oc-lib/tools" | ||||
| ) | ||||
|  | ||||
| type resourceMongoAccessor[T ResourceInterface] struct { | ||||
| type ResourceMongoAccessor[T ResourceInterface] struct { | ||||
| 	utils.AbstractAccessor // AbstractAccessor contains the basic fields of an accessor (model, caller) | ||||
| 	generateData           func() utils.DBObject | ||||
| } | ||||
|  | ||||
| // New creates a new instance of the computeMongoAccessor | ||||
| func NewAccessor[T ResourceInterface](t tools.DataType, request *tools.APIRequest, g func() utils.DBObject) *resourceMongoAccessor[T] { | ||||
| func NewAccessor[T ResourceInterface](t tools.DataType, request *tools.APIRequest, g func() utils.DBObject) *ResourceMongoAccessor[T] { | ||||
| 	if !slices.Contains([]tools.DataType{tools.COMPUTE_RESOURCE, tools.STORAGE_RESOURCE, tools.PROCESSING_RESOURCE, tools.WORKFLOW_RESOURCE, tools.DATA_RESOURCE}, t) { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &resourceMongoAccessor[T]{ | ||||
| 	return &ResourceMongoAccessor[T]{ | ||||
| 		AbstractAccessor: utils.AbstractAccessor{ | ||||
| 			Logger:  logs.CreateLogger(t.String()), // Create a logger with the data type | ||||
| 			Request: request, | ||||
| @@ -32,39 +32,39 @@ func NewAccessor[T ResourceInterface](t tools.DataType, request *tools.APIReques | ||||
| /* | ||||
| * Nothing special here, just the basic CRUD operations | ||||
|  */ | ||||
| func (dca *resourceMongoAccessor[T]) DeleteOne(id string) (utils.DBObject, int, error) { | ||||
| func (dca *ResourceMongoAccessor[T]) DeleteOne(id string) (utils.DBObject, int, error) { | ||||
| 	return utils.GenericDeleteOne(id, dca) | ||||
| } | ||||
|  | ||||
| func (dca *resourceMongoAccessor[T]) UpdateOne(set utils.DBObject, id string) (utils.DBObject, int, error) { | ||||
| func (dca *ResourceMongoAccessor[T]) UpdateOne(set utils.DBObject, id string) (utils.DBObject, int, error) { | ||||
| 	set.(T).Trim() | ||||
| 	return utils.GenericUpdateOne(set, id, dca, dca.generateData()) | ||||
| } | ||||
|  | ||||
| func (dca *resourceMongoAccessor[T]) StoreOne(data utils.DBObject) (utils.DBObject, int, error) { | ||||
| func (dca *ResourceMongoAccessor[T]) StoreOne(data utils.DBObject) (utils.DBObject, int, error) { | ||||
| 	data.(T).Trim() | ||||
| 	return utils.GenericStoreOne(data, dca) | ||||
| } | ||||
|  | ||||
| func (dca *resourceMongoAccessor[T]) CopyOne(data utils.DBObject) (utils.DBObject, int, error) { | ||||
| func (dca *ResourceMongoAccessor[T]) CopyOne(data utils.DBObject) (utils.DBObject, int, error) { | ||||
| 	return dca.StoreOne(data) | ||||
| } | ||||
|  | ||||
| func (dca *resourceMongoAccessor[T]) LoadOne(id string) (utils.DBObject, int, error) { | ||||
| func (dca *ResourceMongoAccessor[T]) LoadOne(id string) (utils.DBObject, int, error) { | ||||
| 	return utils.GenericLoadOne[T](id, func(d utils.DBObject) (utils.DBObject, int, error) { | ||||
| 		d.(T).SetAllowedInstances(dca.Request) | ||||
| 		return d, 200, nil | ||||
| 	}, dca) | ||||
| } | ||||
|  | ||||
| func (wfa *resourceMongoAccessor[T]) LoadAll(isDraft bool) ([]utils.ShallowDBObject, int, error) { | ||||
| func (wfa *ResourceMongoAccessor[T]) LoadAll(isDraft bool) ([]utils.ShallowDBObject, int, error) { | ||||
| 	return utils.GenericLoadAll[T](func(d utils.DBObject) utils.ShallowDBObject { | ||||
| 		d.(T).SetAllowedInstances(wfa.Request) | ||||
| 		return d | ||||
| 	}, isDraft, wfa) | ||||
| } | ||||
|  | ||||
| func (wfa *resourceMongoAccessor[T]) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) { | ||||
| func (wfa *ResourceMongoAccessor[T]) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) { | ||||
| 	if filters == nil && search == "*" { | ||||
| 		return utils.GenericLoadAll[T](func(d utils.DBObject) utils.ShallowDBObject { | ||||
| 			d.(T).SetAllowedInstances(wfa.Request) | ||||
| @@ -78,7 +78,7 @@ func (wfa *resourceMongoAccessor[T]) Search(filters *dbs.Filters, search string, | ||||
| 		}, isDraft, wfa) | ||||
| } | ||||
|  | ||||
| func (abs *resourceMongoAccessor[T]) getResourceFilter(search string) *dbs.Filters { | ||||
| func (abs *ResourceMongoAccessor[T]) getResourceFilter(search string) *dbs.Filters { | ||||
| 	return &dbs.Filters{ | ||||
| 		Or: map[string][]dbs.Filter{ // filter by like name, short_description, description, owner, url if no filters are provided | ||||
| 			"abstractintanciatedresource.abstractresource.abstractobject.name":       {{Operator: dbs.LIKE.String(), Value: search}}, | ||||
|   | ||||
| @@ -183,9 +183,9 @@ func (r *PricedStorageResource) GetPrice() (float64, error) { | ||||
| 		if len(r.PricingProfiles) == 0 { | ||||
| 			return 0, errors.New("pricing profile must be set on Priced Storage" + r.ResourceID) | ||||
| 		} | ||||
| 		r.SelectedPricing = &r.PricingProfiles[0] | ||||
| 		r.SelectedPricing = r.PricingProfiles[0] | ||||
| 	} | ||||
| 	pricing := *r.SelectedPricing | ||||
| 	pricing := r.SelectedPricing | ||||
| 	var err error | ||||
| 	amountOfData := float64(1) | ||||
| 	if pricing.GetOverrideStrategyValue() >= 0 { | ||||
|   | ||||
							
								
								
									
										119
									
								
								models/resources/tests/compute_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								models/resources/tests/compute_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| package resources_test | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/common/models" | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/common/pricing" | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/resources" | ||||
| 	"cloud.o-forge.io/core/oc-lib/tools" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| ) | ||||
|  | ||||
| func TestComputeResource_GetType(t *testing.T) { | ||||
| 	r := &resources.ComputeResource{} | ||||
| 	assert.Equal(t, tools.COMPUTE_RESOURCE.String(), r.GetType()) | ||||
| } | ||||
|  | ||||
| func TestComputeResource_GetAccessor(t *testing.T) { | ||||
| 	req := &tools.APIRequest{} | ||||
| 	cr := &resources.ComputeResource{} | ||||
| 	accessor := cr.GetAccessor(req) | ||||
| 	assert.NotNil(t, accessor) | ||||
| } | ||||
|  | ||||
| func TestComputeResource_ConvertToPricedResource(t *testing.T) { | ||||
| 	req := &tools.APIRequest{} | ||||
| 	cr := &resources.ComputeResource{} | ||||
| 	cr.UUID = "comp123" | ||||
| 	cr.AbstractInstanciatedResource.UUID = cr.UUID | ||||
| 	result := cr.ConvertToPricedResource(tools.COMPUTE_RESOURCE, req) | ||||
| 	assert.NotNil(t, result) | ||||
| 	assert.IsType(t, &resources.PricedComputeResource{}, result) | ||||
| } | ||||
|  | ||||
| func TestComputeResourcePricingProfile_GetPrice_CPUs(t *testing.T) { | ||||
| 	start := time.Now() | ||||
| 	end := start.Add(1 * time.Hour) | ||||
| 	profile := resources.ComputeResourcePricingProfile{ | ||||
| 		CPUsPrices: map[string]float64{"Xeon": 2.0}, | ||||
| 		ExploitPricingProfile: pricing.ExploitPricingProfile[pricing.TimePricingStrategy]{ | ||||
| 			AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{ | ||||
| 				Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{Price: 1.0}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	price, err := profile.GetPrice(2, 3600, start, end, "cpus", "Xeon") | ||||
| 	require.NoError(t, err) | ||||
| 	assert.Greater(t, price, float64(0)) | ||||
| } | ||||
|  | ||||
| func TestComputeResourcePricingProfile_GetPrice_InvalidParams(t *testing.T) { | ||||
| 	profile := resources.ComputeResourcePricingProfile{} | ||||
| 	_, err := profile.GetPrice(1, 3600, time.Now(), time.Now()) | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Equal(t, "params must be set", err.Error()) | ||||
| } | ||||
|  | ||||
| func TestPricedComputeResource_GetPrice(t *testing.T) { | ||||
| 	start := time.Now() | ||||
| 	end := start.Add(1 * time.Hour) | ||||
| 	profile := &resources.ComputeResourcePricingProfile{ | ||||
| 		CPUsPrices: map[string]float64{"Xeon": 1.0}, | ||||
| 		GPUsPrices: map[string]float64{"Tesla": 2.0}, | ||||
| 		RAMPrice:   0.5, | ||||
| 		ExploitPricingProfile: pricing.ExploitPricingProfile[pricing.TimePricingStrategy]{ | ||||
| 			AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{ | ||||
| 				Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{Price: 1.0}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	r := resources.PricedComputeResource{ | ||||
| 		PricedResource: resources.PricedResource{ | ||||
| 			ResourceID:               "comp456", | ||||
| 			PricingProfiles:          []pricing.PricingProfileITF{profile}, | ||||
| 			UsageStart:               &start, | ||||
| 			UsageEnd:                 &end, | ||||
| 			ExplicitBookingDurationS: 3600, | ||||
| 		}, | ||||
| 		CPUsLocated: map[string]float64{"Xeon": 2}, | ||||
| 		GPUsLocated: map[string]float64{"Tesla": 1}, | ||||
| 		RAMLocated:  4, | ||||
| 	} | ||||
|  | ||||
| 	price, err := r.GetPrice() | ||||
| 	require.NoError(t, err) | ||||
| 	assert.Greater(t, price, float64(0)) | ||||
| } | ||||
|  | ||||
| func TestPricedComputeResource_GetPrice_MissingProfile(t *testing.T) { | ||||
| 	r := resources.PricedComputeResource{ | ||||
| 		PricedResource: resources.PricedResource{ | ||||
| 			ResourceID: "comp789", | ||||
| 		}, | ||||
| 	} | ||||
| 	_, err := r.GetPrice() | ||||
| 	require.Error(t, err) | ||||
| 	assert.Contains(t, err.Error(), "pricing profile must be set") | ||||
| } | ||||
|  | ||||
| func TestPricedComputeResource_FillWithDefaultProcessingUsage(t *testing.T) { | ||||
| 	usage := &resources.ProcessingUsage{ | ||||
| 		CPUs: map[string]*models.CPU{"t": {Model: "Xeon", Cores: 4}}, | ||||
| 		GPUs: map[string]*models.GPU{"t1": {Model: "Tesla"}}, | ||||
| 		RAM:  &models.RAM{SizeGb: 16}, | ||||
| 	} | ||||
| 	r := &resources.PricedComputeResource{ | ||||
| 		CPUsLocated: make(map[string]float64), | ||||
| 		GPUsLocated: make(map[string]float64), | ||||
| 		RAMLocated:  0, | ||||
| 	} | ||||
| 	r.FillWithDefaultProcessingUsage(usage) | ||||
| 	assert.Equal(t, float64(4), r.CPUsLocated["Xeon"]) | ||||
| 	assert.Equal(t, float64(1), r.GPUsLocated["Tesla"]) | ||||
| 	assert.Equal(t, float64(16), r.RAMLocated) | ||||
| } | ||||
							
								
								
									
										125
									
								
								models/resources/tests/data_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								models/resources/tests/data_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| package resources_test | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/common/models" | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/common/pricing" | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/resources" | ||||
| 	"cloud.o-forge.io/core/oc-lib/tools" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| ) | ||||
|  | ||||
| func TestDataResource_GetType(t *testing.T) { | ||||
| 	d := &resources.DataResource{} | ||||
| 	assert.Equal(t, tools.DATA_RESOURCE.String(), d.GetType()) | ||||
| } | ||||
|  | ||||
| func TestDataResource_GetAccessor(t *testing.T) { | ||||
| 	req := &tools.APIRequest{} | ||||
| 	acc := (&resources.DataResource{}).GetAccessor(req) | ||||
| 	assert.NotNil(t, acc) | ||||
| } | ||||
|  | ||||
| func TestDataResource_ConvertToPricedResource(t *testing.T) { | ||||
| 	d := &resources.DataResource{} | ||||
| 	d.UUID = "123" | ||||
| 	res := d.ConvertToPricedResource(tools.DATA_RESOURCE, &tools.APIRequest{}) | ||||
| 	assert.IsType(t, &resources.PricedDataResource{}, res) | ||||
|  | ||||
| 	nilRes := d.ConvertToPricedResource(tools.PROCESSING_RESOURCE, &tools.APIRequest{}) | ||||
| 	assert.Nil(t, nilRes) | ||||
| } | ||||
|  | ||||
| func TestDataInstance_StoreDraftDefault(t *testing.T) { | ||||
| 	di := &resources.DataInstance{ | ||||
| 		Source: "test-src", | ||||
| 		ResourceInstance: resources.ResourceInstance[*resources.DataResourcePartnership]{ | ||||
| 			Env: []models.Param{}, | ||||
| 		}, | ||||
| 	} | ||||
| 	di.StoreDraftDefault() | ||||
| 	assert.Len(t, di.ResourceInstance.Env, 1) | ||||
| 	assert.Equal(t, "source", di.ResourceInstance.Env[0].Attr) | ||||
| 	assert.Equal(t, "test-src", di.ResourceInstance.Env[0].Value) | ||||
|  | ||||
| 	// Call again, should not duplicate | ||||
| 	di.StoreDraftDefault() | ||||
| 	assert.Len(t, di.ResourceInstance.Env, 1) | ||||
| } | ||||
|  | ||||
| func TestDataResourcePricingStrategy_GetQuantity(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		strategy resources.DataResourcePricingStrategy | ||||
| 		input    float64 | ||||
| 		expected float64 | ||||
| 	}{ | ||||
| 		{resources.PER_DOWNLOAD, 1, 1}, | ||||
| 		{resources.PER_TB_DOWNLOADED, 1, 1000}, | ||||
| 		{resources.PER_GB_DOWNLOADED, 2.5, 2.5}, | ||||
| 		{resources.PER_MB_DOWNLOADED, 1, 0.001}, | ||||
| 		{resources.PER_KB_DOWNLOADED, 1, 0.000001}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tt := range tests { | ||||
| 		q, err := tt.strategy.GetQuantity(tt.input) | ||||
| 		require.NoError(t, err) | ||||
| 		assert.InDelta(t, tt.expected, q, 1e-9) | ||||
| 	} | ||||
|  | ||||
| 	_, err := resources.DataResourcePricingStrategy(999).GetQuantity(1) | ||||
| 	assert.Error(t, err) | ||||
| } | ||||
|  | ||||
| func TestDataResourcePricingProfile_IsPurchased(t *testing.T) { | ||||
| 	profile := &resources.DataResourcePricingProfile{} | ||||
| 	profile.Pricing.BuyingStrategy = pricing.PAY_PER_USE | ||||
| 	assert.False(t, profile.IsPurchased()) | ||||
|  | ||||
| 	profile.Pricing.BuyingStrategy = pricing.SUBSCRIPTION | ||||
| 	assert.True(t, profile.IsPurchased()) | ||||
| } | ||||
|  | ||||
| func TestPricedDataResource_GetPrice(t *testing.T) { | ||||
| 	now := time.Now() | ||||
| 	later := now.Add(1 * time.Hour) | ||||
| 	mockPrice := 42.0 | ||||
|  | ||||
| 	pricingProfile := &resources.DataResourcePricingProfile{AccessPricingProfile: pricing.AccessPricingProfile[resources.DataResourcePricingStrategy]{ | ||||
| 		Pricing: pricing.PricingStrategy[resources.DataResourcePricingStrategy]{Price: 42.0}}, | ||||
| 	} | ||||
| 	pricingProfile.Pricing.OverrideStrategy = resources.PER_GB_DOWNLOADED | ||||
|  | ||||
| 	r := &resources.PricedDataResource{ | ||||
| 		PricedResource: resources.PricedResource{ | ||||
| 			UsageStart: &now, | ||||
| 			UsageEnd:   &later, | ||||
| 			PricingProfiles: []pricing.PricingProfileITF{ | ||||
| 				pricingProfile, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	price, err := r.GetPrice() | ||||
| 	require.NoError(t, err) | ||||
| 	assert.Equal(t, mockPrice, price) | ||||
| } | ||||
|  | ||||
| func TestPricedDataResource_GetPrice_NoProfiles(t *testing.T) { | ||||
| 	r := &resources.PricedDataResource{ | ||||
| 		PricedResource: resources.PricedResource{ | ||||
| 			ResourceID: "test-resource", | ||||
| 		}, | ||||
| 	} | ||||
| 	_, err := r.GetPrice() | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Contains(t, err.Error(), "pricing profile must be set") | ||||
| } | ||||
|  | ||||
| func TestPricedDataResource_GetType(t *testing.T) { | ||||
| 	r := &resources.PricedDataResource{} | ||||
| 	assert.Equal(t, tools.DATA_RESOURCE, r.GetType()) | ||||
| } | ||||
							
								
								
									
										142
									
								
								models/resources/tests/priced_resource_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								models/resources/tests/priced_resource_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| package resources_test | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
|  | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/common/pricing" | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/resources" | ||||
| 	"cloud.o-forge.io/core/oc-lib/tools" | ||||
| ) | ||||
|  | ||||
| // ---- Mock PricingProfile ---- | ||||
|  | ||||
| type MockPricingProfile struct { | ||||
| 	pricing.PricingProfileITF | ||||
| 	Purchased  bool | ||||
| 	ReturnErr  bool | ||||
| 	ReturnCost float64 | ||||
| } | ||||
|  | ||||
| func (m *MockPricingProfile) IsPurchased() bool { | ||||
| 	return m.Purchased | ||||
| } | ||||
|  | ||||
| func (m *MockPricingProfile) GetPrice(amount float64, explicitDuration float64, start time.Time, end time.Time, _ ...string) (float64, error) { | ||||
| 	if m.ReturnErr { | ||||
| 		return 0, errors.New("mock error") | ||||
| 	} | ||||
| 	return m.ReturnCost, nil | ||||
| } | ||||
|  | ||||
| // ---- Tests ---- | ||||
|  | ||||
| func TestGetIDAndCreatorAndType(t *testing.T) { | ||||
| 	r := resources.PricedResource{ | ||||
| 		ResourceID:   "res-123", | ||||
| 		CreatorID:    "user-abc", | ||||
| 		ResourceType: tools.DATA_RESOURCE, | ||||
| 	} | ||||
| 	assert.Equal(t, "res-123", r.GetID()) | ||||
| 	assert.Equal(t, "user-abc", r.GetCreatorID()) | ||||
| 	assert.Equal(t, tools.DATA_RESOURCE, r.GetType()) | ||||
| } | ||||
|  | ||||
| func TestIsPurchased(t *testing.T) { | ||||
| 	t.Run("nil selected pricing returns false", func(t *testing.T) { | ||||
| 		r := &resources.PricedResource{} | ||||
| 		assert.False(t, r.IsPurchased()) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("returns true if pricing profile is purchased", func(t *testing.T) { | ||||
| 		mock := &MockPricingProfile{Purchased: true} | ||||
| 		r := &resources.PricedResource{SelectedPricing: mock} | ||||
| 		assert.True(t, r.IsPurchased()) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestGetAndSetLocationStartEnd(t *testing.T) { | ||||
| 	r := &resources.PricedResource{} | ||||
|  | ||||
| 	now := time.Now() | ||||
| 	r.SetLocationStart(now) | ||||
| 	r.SetLocationEnd(now.Add(2 * time.Hour)) | ||||
|  | ||||
| 	assert.Equal(t, now, *r.GetLocationStart()) | ||||
| 	assert.Equal(t, now.Add(2*time.Hour), *r.GetLocationEnd()) | ||||
| } | ||||
|  | ||||
| func TestGetExplicitDurationInS(t *testing.T) { | ||||
| 	t.Run("uses explicit duration if set", func(t *testing.T) { | ||||
| 		r := &resources.PricedResource{ExplicitBookingDurationS: 3600} | ||||
| 		assert.Equal(t, 3600.0, r.GetExplicitDurationInS()) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("computes duration from start and end", func(t *testing.T) { | ||||
| 		start := time.Now() | ||||
| 		end := start.Add(2 * time.Hour) | ||||
| 		r := &resources.PricedResource{UsageStart: &start, UsageEnd: &end} | ||||
| 		assert.InDelta(t, 7200.0, r.GetExplicitDurationInS(), 0.1) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("defaults to 1 hour when times not set", func(t *testing.T) { | ||||
| 		r := &resources.PricedResource{} | ||||
| 		assert.InDelta(t, 3600.0, r.GetExplicitDurationInS(), 0.1) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestGetPrice(t *testing.T) { | ||||
| 	t.Run("returns error if no pricing profile", func(t *testing.T) { | ||||
| 		r := &resources.PricedResource{ResourceID: "no-profile"} | ||||
| 		price, err := r.GetPrice() | ||||
| 		require.Error(t, err) | ||||
| 		assert.Contains(t, err.Error(), "pricing profile must be set") | ||||
| 		assert.Equal(t, 0.0, price) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("uses first profile if selected is nil", func(t *testing.T) { | ||||
| 		start := time.Now() | ||||
| 		end := start.Add(30 * time.Minute) | ||||
| 		mock := &MockPricingProfile{ReturnCost: 42.0} | ||||
| 		r := &resources.PricedResource{ | ||||
| 			PricingProfiles: []pricing.PricingProfileITF{mock}, | ||||
| 			UsageStart:      &start, | ||||
| 			UsageEnd:        &end, | ||||
| 		} | ||||
| 		price, err := r.GetPrice() | ||||
| 		require.NoError(t, err) | ||||
| 		assert.Equal(t, 42.0, price) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("returns error if profile GetPrice fails", func(t *testing.T) { | ||||
| 		start := time.Now() | ||||
| 		end := start.Add(1 * time.Hour) | ||||
| 		mock := &MockPricingProfile{ReturnErr: true} | ||||
| 		r := &resources.PricedResource{ | ||||
| 			SelectedPricing: mock, | ||||
| 			UsageStart:      &start, | ||||
| 			UsageEnd:        &end, | ||||
| 		} | ||||
| 		price, err := r.GetPrice() | ||||
| 		require.Error(t, err) | ||||
| 		assert.Equal(t, 0.0, price) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("uses SelectedPricing if set", func(t *testing.T) { | ||||
| 		start := time.Now() | ||||
| 		end := start.Add(1 * time.Hour) | ||||
| 		mock := &MockPricingProfile{ReturnCost: 10.0} | ||||
| 		r := &resources.PricedResource{ | ||||
| 			SelectedPricing: mock, | ||||
| 			UsageStart:      &start, | ||||
| 			UsageEnd:        &end, | ||||
| 		} | ||||
| 		price, err := r.GetPrice() | ||||
| 		require.NoError(t, err) | ||||
| 		assert.Equal(t, 10.0, price) | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										115
									
								
								models/resources/tests/processing_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								models/resources/tests/processing_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| package resources_test | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/common/pricing" | ||||
| 	. "cloud.o-forge.io/core/oc-lib/models/resources" | ||||
| 	"cloud.o-forge.io/core/oc-lib/tools" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestProcessingResource_GetType(t *testing.T) { | ||||
| 	r := &ProcessingResource{} | ||||
| 	assert.Equal(t, tools.PROCESSING_RESOURCE.String(), r.GetType()) | ||||
| } | ||||
|  | ||||
| func TestPricedProcessingResource_GetType(t *testing.T) { | ||||
| 	r := &PricedProcessingResource{} | ||||
| 	assert.Equal(t, tools.PROCESSING_RESOURCE, r.GetType()) | ||||
| } | ||||
|  | ||||
| func TestPricedProcessingResource_GetExplicitDurationInS(t *testing.T) { | ||||
| 	now := time.Now() | ||||
| 	after := now.Add(2 * time.Hour) | ||||
|  | ||||
| 	tests := []struct { | ||||
| 		name     string | ||||
| 		input    PricedProcessingResource | ||||
| 		expected float64 | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "Service without explicit duration", | ||||
| 			input: PricedProcessingResource{ | ||||
| 				IsService: true, | ||||
| 			}, | ||||
| 			expected: -1, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Nil start time, non-service", | ||||
| 			input: PricedProcessingResource{ | ||||
| 				PricedResource: PricedResource{ | ||||
| 					UsageStart: nil, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expected: float64((1 * time.Hour).Seconds()), | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Duration computed from start and end", | ||||
| 			input: PricedProcessingResource{ | ||||
| 				PricedResource: PricedResource{ | ||||
| 					UsageStart: &now, | ||||
| 					UsageEnd:   &after, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expected: float64((2 * time.Hour).Seconds()), | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Explicit duration takes precedence", | ||||
| 			input: PricedProcessingResource{ | ||||
| 				PricedResource: PricedResource{ | ||||
| 					ExplicitBookingDurationS: 1337, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expected: 1337, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range tests { | ||||
| 		t.Run(test.name, func(t *testing.T) { | ||||
| 			assert.Equal(t, test.expected, test.input.GetExplicitDurationInS()) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestProcessingResource_GetAccessor(t *testing.T) { | ||||
| 	request := &tools.APIRequest{} | ||||
| 	r := &ProcessingResource{} | ||||
| 	acc := r.GetAccessor(request) | ||||
| 	assert.NotNil(t, acc) | ||||
| } | ||||
|  | ||||
| func TestProcessingResourcePricingProfile_GetPrice(t *testing.T) { | ||||
| 	start := time.Now() | ||||
| 	end := start.Add(2 * time.Hour) | ||||
| 	mockPricing := pricing.AccessPricingProfile[pricing.TimePricingStrategy]{ | ||||
| 		Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{ | ||||
| 			Price: 100.0, | ||||
| 		}, | ||||
| 	} | ||||
| 	profile := &ProcessingResourcePricingProfile{mockPricing} | ||||
| 	price, err := profile.GetPrice(0, 0, start, end) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, 100.0, price) | ||||
| } | ||||
|  | ||||
| func TestProcessingResourcePricingProfile_IsPurchased(t *testing.T) { | ||||
| 	nonPurchased := &ProcessingResourcePricingProfile{ | ||||
| 		AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{ | ||||
| 			Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{ | ||||
| 				BuyingStrategy: pricing.PAY_PER_USE, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	assert.False(t, nonPurchased.IsPurchased()) | ||||
|  | ||||
| 	purchased := &ProcessingResourcePricingProfile{ | ||||
| 		AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{ | ||||
| 			Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{ | ||||
| 				BuyingStrategy: pricing.UNLIMITED, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	assert.True(t, purchased.IsPurchased()) | ||||
| } | ||||
							
								
								
									
										106
									
								
								models/resources/tests/resource_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								models/resources/tests/resource_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| package resources_test | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/common/pricing" | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/resources" | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/utils" | ||||
| 	"cloud.o-forge.io/core/oc-lib/tools" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| type MockInstance struct { | ||||
| 	ID   string | ||||
| 	Name string | ||||
| 	resources.ResourceInstance[*MockPartner] | ||||
| } | ||||
|  | ||||
| func (m *MockInstance) GetID() string                                                    { return m.ID } | ||||
| func (m *MockInstance) GetName() string                                                  { return m.Name } | ||||
| func (m *MockInstance) ClearEnv()                                                        {} | ||||
| func (m *MockInstance) ClearPeerGroups()                                                 {} | ||||
| func (m *MockInstance) GetPricingsProfiles(string, []string) []pricing.PricingProfileITF { return nil } | ||||
| func (m *MockInstance) GetPeerGroups() ([]resources.ResourcePartnerITF, []map[string][]string) { | ||||
| 	return nil, []map[string][]string{ | ||||
| 		{"peer1": {"group1"}}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type MockPartner struct { | ||||
| 	groups map[string][]string | ||||
| } | ||||
|  | ||||
| func (m *MockPartner) GetPeerGroups() map[string][]string { | ||||
| 	return m.groups | ||||
| } | ||||
| func (m *MockPartner) ClearPeerGroups() {} | ||||
| func (m *MockPartner) GetPricingsProfiles(string, []string) []pricing.PricingProfileITF { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type MockDBObject struct { | ||||
| 	utils.AbstractObject | ||||
| 	isDraft bool | ||||
| } | ||||
|  | ||||
| func (m *MockDBObject) IsDrafted() bool { | ||||
| 	return m.isDraft | ||||
| } | ||||
|  | ||||
| func TestGetSelectedInstance_WithValidIndex(t *testing.T) { | ||||
| 	index := 1 | ||||
| 	inst1 := &MockInstance{ID: "1"} | ||||
| 	inst2 := &MockInstance{ID: "2"} | ||||
| 	resource := &resources.AbstractInstanciatedResource[*MockInstance]{ | ||||
| 		AbstractResource: resources.AbstractResource{SelectedInstanceIndex: &index}, | ||||
| 		Instances:        []*MockInstance{inst1, inst2}, | ||||
| 	} | ||||
| 	result := resource.GetSelectedInstance() | ||||
| 	assert.Equal(t, inst2, result) | ||||
| } | ||||
|  | ||||
| func TestGetSelectedInstance_NoIndex(t *testing.T) { | ||||
| 	inst := &MockInstance{ID: "1"} | ||||
| 	resource := &resources.AbstractInstanciatedResource[*MockInstance]{ | ||||
| 		Instances: []*MockInstance{inst}, | ||||
| 	} | ||||
| 	result := resource.GetSelectedInstance() | ||||
| 	assert.Equal(t, inst, result) | ||||
| } | ||||
|  | ||||
| func TestCanUpdate_WhenOnlyStateDiffers(t *testing.T) { | ||||
| 	resource := &resources.AbstractResource{AbstractObject: utils.AbstractObject{IsDraft: false}} | ||||
| 	set := &MockDBObject{isDraft: true} | ||||
| 	canUpdate, updated := resource.CanUpdate(set) | ||||
| 	assert.True(t, canUpdate) | ||||
| 	assert.Equal(t, set, updated) | ||||
| } | ||||
|  | ||||
| func TestVerifyAuthAction_WithMatchingGroup(t *testing.T) { | ||||
| 	inst := &MockInstance{ | ||||
| 		ResourceInstance: resources.ResourceInstance[*MockPartner]{ | ||||
| 			Partnerships: []*MockPartner{ | ||||
| 				{groups: map[string][]string{"peer1": {"group1"}}}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	req := &tools.APIRequest{PeerID: "peer1", Groups: []string{"group1"}} | ||||
| 	result := resources.VerifyAuthAction([]*MockInstance{inst}, req) | ||||
| 	assert.Len(t, result, 1) | ||||
| } | ||||
|  | ||||
| type FakeResource struct { | ||||
| 	resources.AbstractInstanciatedResource[*MockInstance] | ||||
| } | ||||
|  | ||||
| func (f *FakeResource) Trim()                                 {} | ||||
| func (f *FakeResource) SetAllowedInstances(*tools.APIRequest) {} | ||||
| func (f *FakeResource) VerifyAuth(*tools.APIRequest) bool     { return true } | ||||
|  | ||||
| func TestNewAccessor_ReturnsValid(t *testing.T) { | ||||
| 	acc := resources.NewAccessor[*FakeResource](tools.COMPUTE_RESOURCE, &tools.APIRequest{}, func() utils.DBObject { | ||||
| 		return &FakeResource{} | ||||
| 	}) | ||||
| 	assert.NotNil(t, acc) | ||||
| } | ||||
							
								
								
									
										149
									
								
								models/resources/tests/storage_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								models/resources/tests/storage_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | ||||
| package resources_test | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/common/models" | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/common/pricing" | ||||
| 	"cloud.o-forge.io/core/oc-lib/tools" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
|  | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/resources" | ||||
| ) | ||||
|  | ||||
| func TestStorageResource_GetType(t *testing.T) { | ||||
| 	res := &resources.StorageResource{} | ||||
| 	assert.Equal(t, tools.STORAGE_RESOURCE.String(), res.GetType()) | ||||
| } | ||||
|  | ||||
| func TestStorageResource_GetAccessor(t *testing.T) { | ||||
| 	res := &resources.StorageResource{} | ||||
| 	req := &tools.APIRequest{} | ||||
| 	accessor := res.GetAccessor(req) | ||||
| 	assert.NotNil(t, accessor) | ||||
| } | ||||
|  | ||||
| func TestStorageResource_ConvertToPricedResource_ValidType(t *testing.T) { | ||||
| 	res := &resources.StorageResource{} | ||||
| 	res.AbstractInstanciatedResource.CreatorID = "creator" | ||||
| 	res.AbstractInstanciatedResource.UUID = "res-id" | ||||
| 	priced := res.ConvertToPricedResource(tools.STORAGE_RESOURCE, &tools.APIRequest{}) | ||||
| 	assert.NotNil(t, priced) | ||||
| 	assert.IsType(t, &resources.PricedStorageResource{}, priced) | ||||
| } | ||||
|  | ||||
| func TestStorageResource_ConvertToPricedResource_InvalidType(t *testing.T) { | ||||
| 	res := &resources.StorageResource{} | ||||
| 	priced := res.ConvertToPricedResource(tools.COMPUTE_RESOURCE, &tools.APIRequest{}) | ||||
| 	assert.Nil(t, priced) | ||||
| } | ||||
|  | ||||
| func TestStorageResourceInstance_ClearEnv(t *testing.T) { | ||||
| 	inst := &resources.StorageResourceInstance{ | ||||
| 		Credentials: &resources.Credentials{Login: "test"}, | ||||
| 		ResourceInstance: resources.ResourceInstance[*resources.StorageResourcePartnership]{ | ||||
| 			Env:     []models.Param{{Attr: "A"}}, | ||||
| 			Inputs:  []models.Param{{Attr: "B"}}, | ||||
| 			Outputs: []models.Param{{Attr: "C"}}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	inst.ClearEnv() | ||||
| 	assert.Nil(t, inst.Credentials) | ||||
| 	assert.Empty(t, inst.Env) | ||||
| 	assert.Empty(t, inst.Inputs) | ||||
| 	assert.Empty(t, inst.Outputs) | ||||
| } | ||||
|  | ||||
| func TestStorageResourceInstance_StoreDraftDefault(t *testing.T) { | ||||
| 	inst := &resources.StorageResourceInstance{ | ||||
| 		Source: "my-source", | ||||
| 		ResourceInstance: resources.ResourceInstance[*resources.StorageResourcePartnership]{ | ||||
| 			Env: []models.Param{}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	inst.StoreDraftDefault() | ||||
| 	assert.Len(t, inst.Env, 1) | ||||
| 	assert.Equal(t, "source", inst.Env[0].Attr) | ||||
| 	assert.Equal(t, "my-source", inst.Env[0].Value) | ||||
| 	assert.True(t, inst.Env[0].Readonly) | ||||
| } | ||||
|  | ||||
| func TestStorageResourcePricingStrategy_GetQuantity(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		strategy resources.StorageResourcePricingStrategy | ||||
| 		dataGB   float64 | ||||
| 		expect   float64 | ||||
| 	}{ | ||||
| 		{resources.PER_DATA_STORED, 1.2, 1.2}, | ||||
| 		{resources.PER_TB_STORED, 1.2, 1200}, | ||||
| 		{resources.PER_GB_STORED, 2.5, 2.5}, | ||||
| 		{resources.PER_MB_STORED, 1.0, 1000}, | ||||
| 		{resources.PER_KB_STORED, 0.1, 100000}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tt := range tests { | ||||
| 		q, err := tt.strategy.GetQuantity(tt.dataGB) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Equal(t, tt.expect, q) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestStorageResourcePricingStrategy_GetQuantity_Invalid(t *testing.T) { | ||||
| 	invalid := resources.StorageResourcePricingStrategy(99) | ||||
| 	q, err := invalid.GetQuantity(1.0) | ||||
| 	assert.Error(t, err) | ||||
| 	assert.Equal(t, 0.0, q) | ||||
| } | ||||
|  | ||||
| func TestPricedStorageResource_GetPrice_NoProfiles(t *testing.T) { | ||||
| 	res := &resources.PricedStorageResource{ | ||||
| 		PricedResource: resources.PricedResource{ | ||||
| 			ResourceID: "res-id", | ||||
| 		}, | ||||
| 	} | ||||
| 	_, err := res.GetPrice() | ||||
| 	assert.Error(t, err) | ||||
| } | ||||
|  | ||||
| func TestPricedStorageResource_GetPrice_WithPricing(t *testing.T) { | ||||
| 	now := time.Now() | ||||
| 	end := now.Add(2 * time.Hour) | ||||
| 	profile := &resources.StorageResourcePricingProfile{ | ||||
| 		ExploitPricingProfile: pricing.ExploitPricingProfile[resources.StorageResourcePricingStrategy]{ | ||||
| 			AccessPricingProfile: pricing.AccessPricingProfile[resources.StorageResourcePricingStrategy]{ | ||||
| 				Pricing: pricing.PricingStrategy[resources.StorageResourcePricingStrategy]{ | ||||
| 					BuyingStrategy: pricing.PAY_PER_USE, | ||||
| 					Price:          42.0, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	res := &resources.PricedStorageResource{ | ||||
| 		PricedResource: resources.PricedResource{ | ||||
| 			UsageStart:      &now, | ||||
| 			UsageEnd:        &end, | ||||
| 			PricingProfiles: []pricing.PricingProfileITF{profile}, | ||||
| 		}, | ||||
| 		UsageStorageGB: 1.0, | ||||
| 	} | ||||
| 	price, err := res.GetPrice() | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, 42.0, price) | ||||
| } | ||||
|  | ||||
| func TestStorageResourcePricingProfile_IsPurchased(t *testing.T) { | ||||
| 	p := &resources.StorageResourcePricingProfile{ | ||||
| 		ExploitPricingProfile: pricing.ExploitPricingProfile[resources.StorageResourcePricingStrategy]{ | ||||
| 			AccessPricingProfile: pricing.AccessPricingProfile[resources.StorageResourcePricingStrategy]{ | ||||
| 				Pricing: pricing.PricingStrategy[resources.StorageResourcePricingStrategy]{BuyingStrategy: pricing.PAY_PER_USE}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	assert.False(t, p.IsPurchased()) | ||||
|  | ||||
| 	p.Pricing.BuyingStrategy = pricing.UNLIMITED | ||||
| 	assert.True(t, p.IsPurchased()) | ||||
| } | ||||
							
								
								
									
										62
									
								
								models/resources/tests/workflow_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								models/resources/tests/workflow_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| package resources_test | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/utils" | ||||
| 	"cloud.o-forge.io/core/oc-lib/tools" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
|  | ||||
| 	"cloud.o-forge.io/core/oc-lib/models/resources" | ||||
| ) | ||||
|  | ||||
| func TestWorkflowResource_GetType(t *testing.T) { | ||||
| 	w := &resources.WorkflowResource{} | ||||
| 	assert.Equal(t, tools.WORKFLOW_RESOURCE.String(), w.GetType()) | ||||
| } | ||||
|  | ||||
| func TestWorkflowResource_ConvertToPricedResource(t *testing.T) { | ||||
| 	w := &resources.WorkflowResource{ | ||||
| 		AbstractResource: resources.AbstractResource{ | ||||
| 			AbstractObject: utils.AbstractObject{ | ||||
| 				Name:      "Test Workflow", | ||||
| 				UUID:      "workflow-uuid", | ||||
| 				CreatorID: "creator-id", | ||||
| 			}, | ||||
| 			Logo: "logo.png", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	req := &tools.APIRequest{ | ||||
| 		PeerID: "peer-1", | ||||
| 		Groups: []string{"group1"}, | ||||
| 	} | ||||
|  | ||||
| 	pr := w.ConvertToPricedResource(tools.WORKFLOW_RESOURCE, req) | ||||
| 	assert.Equal(t, "creator-id", pr.GetCreatorID()) | ||||
| 	assert.Equal(t, tools.WORKFLOW_RESOURCE, pr.GetType()) | ||||
| } | ||||
|  | ||||
| func TestWorkflowResource_ClearEnv(t *testing.T) { | ||||
| 	w := &resources.WorkflowResource{} | ||||
| 	assert.Equal(t, w, w.ClearEnv()) | ||||
| } | ||||
|  | ||||
| func TestWorkflowResource_Trim(t *testing.T) { | ||||
| 	w := &resources.WorkflowResource{} | ||||
| 	w.Trim() | ||||
| 	// nothing to assert; just test that it doesn't panic | ||||
| } | ||||
|  | ||||
| func TestWorkflowResource_SetAllowedInstances(t *testing.T) { | ||||
| 	w := &resources.WorkflowResource{} | ||||
| 	w.SetAllowedInstances(&tools.APIRequest{}) | ||||
| 	// no-op; just confirm no crash | ||||
| } | ||||
|  | ||||
| func TestWorkflowResource_GetAccessor(t *testing.T) { | ||||
| 	w := &resources.WorkflowResource{} | ||||
| 	request := &tools.APIRequest{} | ||||
| 	accessor := w.GetAccessor(request) | ||||
| 	assert.NotNil(t, accessor) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user