draft test
This commit is contained in:
parent
2a0ab8e549
commit
48299810e0
3
go.mod
Normal file → Executable file
3
go.mod
Normal file → Executable file
@ -10,12 +10,13 @@ require (
|
|||||||
github.com/nats-io/nats.go v1.37.0
|
github.com/nats-io/nats.go v1.37.0
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.10.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
4
go.sum
Normal file → Executable file
4
go.sum
Normal file → Executable file
@ -106,9 +106,13 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
|
|||||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
|
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
This package contains the models used in the application
|
This package contains the models used in the application
|
||||||
It's used to create the models dynamically
|
It's used to create the models dynamically
|
||||||
*/
|
*/
|
||||||
var models = map[string]func() utils.DBObject{
|
var ModelsCatalog = map[string]func() utils.DBObject{
|
||||||
tools.WORKFLOW_RESOURCE.String(): func() utils.DBObject { return &resource.WorkflowResource{} },
|
tools.WORKFLOW_RESOURCE.String(): func() utils.DBObject { return &resource.WorkflowResource{} },
|
||||||
tools.DATA_RESOURCE.String(): func() utils.DBObject { return &resource.DataResource{} },
|
tools.DATA_RESOURCE.String(): func() utils.DBObject { return &resource.DataResource{} },
|
||||||
tools.COMPUTE_RESOURCE.String(): func() utils.DBObject { return &resource.ComputeResource{} },
|
tools.COMPUTE_RESOURCE.String(): func() utils.DBObject { return &resource.ComputeResource{} },
|
||||||
@ -43,8 +43,8 @@ var models = map[string]func() utils.DBObject{
|
|||||||
// Model returns the model object based on the model type
|
// Model returns the model object based on the model type
|
||||||
func Model(model int) utils.DBObject {
|
func Model(model int) utils.DBObject {
|
||||||
log := logs.GetLogger()
|
log := logs.GetLogger()
|
||||||
if _, ok := models[tools.FromInt(model)]; ok {
|
if _, ok := ModelsCatalog[tools.FromInt(model)]; ok {
|
||||||
return models[tools.FromInt(model)]()
|
return ModelsCatalog[tools.FromInt(model)]()
|
||||||
}
|
}
|
||||||
log.Error().Msg("Can't find model " + tools.FromInt(model) + ".")
|
log.Error().Msg("Can't find model " + tools.FromInt(model) + ".")
|
||||||
return nil
|
return nil
|
||||||
@ -53,7 +53,7 @@ func Model(model int) utils.DBObject {
|
|||||||
// GetModelsNames returns the names of the models
|
// GetModelsNames returns the names of the models
|
||||||
func GetModelsNames() []string {
|
func GetModelsNames() []string {
|
||||||
names := []string{}
|
names := []string{}
|
||||||
for name := range models {
|
for name := range ModelsCatalog {
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
return names
|
return names
|
||||||
|
@ -133,7 +133,7 @@ func (o *Order) draftStoreFromModel(scheduler *workflow_execution.WorkflowSchedu
|
|||||||
o.SubOrders[peerOrder.GetID()] = peerOrder
|
o.SubOrders[peerOrder.GetID()] = peerOrder
|
||||||
}
|
}
|
||||||
// search an order with same user name and same session id
|
// search an order with same user name and same session id
|
||||||
err := o.sumUpBill(request)
|
err := o.SumUpBill(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ func (d *Order) GetAccessor(request *tools.APIRequest) utils.Accessor {
|
|||||||
return NewAccessor(request) // Create a new instance of the accessor
|
return NewAccessor(request) // Create a new instance of the accessor
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Order) sumUpBill(request *tools.APIRequest) error {
|
func (d *Order) SumUpBill(request *tools.APIRequest) error {
|
||||||
for _, b := range d.SubOrders {
|
for _, b := range d.SubOrders {
|
||||||
err := b.SumUpBill(request)
|
err := b.SumUpBill(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
0
models/order/tests/order_test.go
Normal file
0
models/order/tests/order_test.go
Normal file
@ -50,9 +50,9 @@ func (p *PeerCache) checkPeerStatus(peerID string, appName string) (*Peer, bool)
|
|||||||
// LaunchPeerExecution launches an execution on a peer
|
// LaunchPeerExecution launches an execution on a peer
|
||||||
// The method contacts the path described by : peer.Url + datatype path (from enums) + replacement of id by dataID
|
// The method contacts the path described by : peer.Url + datatype path (from enums) + replacement of id by dataID
|
||||||
func (p *PeerCache) LaunchPeerExecution(peerID string, dataID string,
|
func (p *PeerCache) LaunchPeerExecution(peerID string, dataID string,
|
||||||
dt tools.DataType, method tools.METHOD, body interface{}, caller *tools.HTTPCaller) (*PeerExecution, error) {
|
dt tools.DataType, method tools.METHOD, body interface{}, caller tools.HTTPCallerITF) (*PeerExecution, error) {
|
||||||
fmt.Println("Launching peer execution on", caller.URLS, dt, method)
|
fmt.Println("Launching peer execution on", caller.GetUrls(), dt, method)
|
||||||
methods := caller.URLS[dt] // Get the methods url of the data type
|
methods := caller.GetUrls()[dt] // Get the methods url of the data type
|
||||||
if m, ok := methods[method]; !ok || m == "" {
|
if m, ok := methods[method]; !ok || m == "" {
|
||||||
return nil, errors.New("Requested method " + method.String() + " not declared in HTTPCaller")
|
return nil, errors.New("Requested method " + method.String() + " not declared in HTTPCaller")
|
||||||
}
|
}
|
||||||
@ -83,14 +83,14 @@ func (p *PeerCache) LaunchPeerExecution(peerID string, dataID string,
|
|||||||
mypeer.FailedExecution = []PeerExecution{} // Reset the failed executions list
|
mypeer.FailedExecution = []PeerExecution{} // Reset the failed executions list
|
||||||
NewShallowAccessor().UpdateOne(mypeer, peerID) // Update the peer in the db
|
NewShallowAccessor().UpdateOne(mypeer, peerID) // Update the peer in the db
|
||||||
for _, v := range tmp { // Retry the failed executions
|
for _, v := range tmp { // Retry the failed executions
|
||||||
go p.exec(v.Url, tools.ToMethod(v.Method), v.Body, caller)
|
go p.Exec(v.Url, tools.ToMethod(v.Method), v.Body, caller)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, p.exec(url, method, body, caller) // Execute the method
|
return nil, p.Exec(url, method, body, caller) // Execute the method
|
||||||
}
|
}
|
||||||
|
|
||||||
// exec executes the method on the peer
|
// exec executes the method on the peer
|
||||||
func (p *PeerCache) exec(url string, method tools.METHOD, body interface{}, caller *tools.HTTPCaller) error {
|
func (p *PeerCache) Exec(url string, method tools.METHOD, body interface{}, caller tools.HTTPCallerITF) error {
|
||||||
var b []byte
|
var b []byte
|
||||||
var err error
|
var err error
|
||||||
if method == tools.POST { // Execute the POST method if it's a POST method
|
if method == tools.POST { // Execute the POST method if it's a POST method
|
||||||
|
@ -11,13 +11,13 @@ import (
|
|||||||
|
|
||||||
type peerMongoAccessor struct {
|
type peerMongoAccessor struct {
|
||||||
utils.AbstractAccessor // AbstractAccessor contains the basic fields of an accessor (model, caller)
|
utils.AbstractAccessor // AbstractAccessor contains the basic fields of an accessor (model, caller)
|
||||||
overrideAuth bool
|
OverrideAuth bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new instance of the peerMongoAccessor
|
// New creates a new instance of the peerMongoAccessor
|
||||||
func NewShallowAccessor() *peerMongoAccessor {
|
func NewShallowAccessor() *peerMongoAccessor {
|
||||||
return &peerMongoAccessor{
|
return &peerMongoAccessor{
|
||||||
overrideAuth: true,
|
OverrideAuth: true,
|
||||||
AbstractAccessor: utils.AbstractAccessor{
|
AbstractAccessor: utils.AbstractAccessor{
|
||||||
Logger: logs.CreateLogger(tools.PEER.String()), // Create a logger with the data type
|
Logger: logs.CreateLogger(tools.PEER.String()), // Create a logger with the data type
|
||||||
Type: tools.PEER,
|
Type: tools.PEER,
|
||||||
@ -27,7 +27,7 @@ func NewShallowAccessor() *peerMongoAccessor {
|
|||||||
|
|
||||||
func NewAccessor(request *tools.APIRequest) *peerMongoAccessor {
|
func NewAccessor(request *tools.APIRequest) *peerMongoAccessor {
|
||||||
return &peerMongoAccessor{
|
return &peerMongoAccessor{
|
||||||
overrideAuth: false,
|
OverrideAuth: false,
|
||||||
AbstractAccessor: utils.AbstractAccessor{
|
AbstractAccessor: utils.AbstractAccessor{
|
||||||
Logger: logs.CreateLogger(tools.PEER.String()), // Create a logger with the data type
|
Logger: logs.CreateLogger(tools.PEER.String()), // Create a logger with the data type
|
||||||
Request: request,
|
Request: request,
|
||||||
@ -37,7 +37,7 @@ func NewAccessor(request *tools.APIRequest) *peerMongoAccessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (wfa *peerMongoAccessor) ShouldVerifyAuth() bool {
|
func (wfa *peerMongoAccessor) ShouldVerifyAuth() bool {
|
||||||
return !wfa.overrideAuth
|
return !wfa.OverrideAuth
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -73,12 +73,12 @@ func (wfa *peerMongoAccessor) LoadAll(isDraft bool) ([]utils.ShallowDBObject, in
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (wfa *peerMongoAccessor) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
func (wfa *peerMongoAccessor) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
||||||
return utils.GenericSearch[*Peer](filters, search, wfa.getDefaultFilter(search),
|
return utils.GenericSearch[*Peer](filters, search, wfa.GetDefaultFilter(search),
|
||||||
func(d utils.DBObject) utils.ShallowDBObject {
|
func(d utils.DBObject) utils.ShallowDBObject {
|
||||||
return d
|
return d
|
||||||
}, isDraft, wfa)
|
}, isDraft, wfa)
|
||||||
}
|
}
|
||||||
func (a *peerMongoAccessor) getDefaultFilter(search string) *dbs.Filters {
|
func (a *peerMongoAccessor) GetDefaultFilter(search string) *dbs.Filters {
|
||||||
if i, err := strconv.Atoi(search); err == nil {
|
if i, err := strconv.Atoi(search); err == nil {
|
||||||
return &dbs.Filters{
|
return &dbs.Filters{
|
||||||
Or: map[string][]dbs.Filter{ // search by name if no filters are provided
|
Or: map[string][]dbs.Filter{ // search by name if no filters are provided
|
||||||
|
100
models/peer/tests/peer_cache_test.go
Normal file
100
models/peer/tests/peer_cache_test.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package peer_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/peer"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/tools"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockHTTPCaller mocks tools.HTTPCaller
|
||||||
|
type MockHTTPCaller struct {
|
||||||
|
mock.Mock
|
||||||
|
URLS map[tools.DataType]map[tools.METHOD]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MockHTTPCaller) GetUrls() map[tools.DataType]map[tools.METHOD]string {
|
||||||
|
return c.URLS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockHTTPCaller) CallPost(url, token string, body interface{}, types ...string) ([]byte, error) {
|
||||||
|
args := m.Called(url, token, body)
|
||||||
|
return args.Get(0).([]byte), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockHTTPCaller) CallGet(url, token string, types ...string) ([]byte, error) {
|
||||||
|
args := m.Called(url, token)
|
||||||
|
return args.Get(0).([]byte), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockHTTPCaller) CallDelete(url, token string) ([]byte, error) {
|
||||||
|
args := m.Called(url, token)
|
||||||
|
return args.Get(0).([]byte), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLaunchPeerExecution_PeerNotReachable(t *testing.T) {
|
||||||
|
cache := &peer.PeerCache{}
|
||||||
|
caller := &MockHTTPCaller{
|
||||||
|
URLS: map[tools.DataType]map[tools.METHOD]string{
|
||||||
|
tools.PEER: {
|
||||||
|
tools.POST: "/execute/:id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
exec, err := cache.LaunchPeerExecution("peer-id", "data-id", tools.PEER, tools.POST, map[string]string{"a": "b"}, caller)
|
||||||
|
assert.Nil(t, exec)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "not reachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecSuccess(t *testing.T) {
|
||||||
|
cache := &peer.PeerCache{}
|
||||||
|
caller := &MockHTTPCaller{}
|
||||||
|
url := "http://mockpeer/resource"
|
||||||
|
response := map[string]interface{}{"result": "ok"}
|
||||||
|
data, _ := json.Marshal(response)
|
||||||
|
|
||||||
|
caller.On("CallPost", url, "", mock.Anything).Return(data, nil)
|
||||||
|
err := cache.Exec(url, tools.POST, map[string]string{"key": "value"}, caller)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
caller.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecReturnsErrorField(t *testing.T) {
|
||||||
|
cache := &peer.PeerCache{}
|
||||||
|
caller := &MockHTTPCaller{}
|
||||||
|
url := "http://mockpeer/resource"
|
||||||
|
response := map[string]interface{}{"error": "something failed"}
|
||||||
|
data, _ := json.Marshal(response)
|
||||||
|
|
||||||
|
caller.On("CallPost", url, "", mock.Anything).Return(data, nil)
|
||||||
|
err := cache.Exec(url, tools.POST, map[string]string{"key": "value"}, caller)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, "something failed", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecInvalidJSON(t *testing.T) {
|
||||||
|
cache := &peer.PeerCache{}
|
||||||
|
caller := &MockHTTPCaller{}
|
||||||
|
url := "http://mockpeer/resource"
|
||||||
|
caller.On("CallPost", url, "", mock.Anything).Return([]byte("{invalid json}"), nil)
|
||||||
|
err := cache.Exec(url, tools.POST, map[string]string{"key": "value"}, caller)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "invalid character")
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockAccessor struct {
|
||||||
|
loadOne func(string) (interface{}, int, error)
|
||||||
|
updateOne func(interface{}, string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockAccessor) LoadOne(id string) (interface{}, int, error) {
|
||||||
|
return m.loadOne(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockAccessor) UpdateOne(i interface{}, id string) error {
|
||||||
|
return m.updateOne(i, id)
|
||||||
|
}
|
127
models/peer/tests/peer_test.go
Normal file
127
models/peer/tests/peer_test.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package peer_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
"cloud.o-forge.io/core/oc-lib/dbs"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/peer"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockAccessor struct {
|
||||||
|
mock.Mock
|
||||||
|
utils.AbstractAccessor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) DeleteOne(id string) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(id)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) UpdateOne(set utils.DBObject, id string) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(set, id)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) StoreOne(data utils.DBObject) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(data)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) LoadOne(id string) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(id)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) LoadAll(isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
||||||
|
args := m.Called(isDraft)
|
||||||
|
return args.Get(0).([]utils.ShallowDBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
||||||
|
args := m.Called(filters, search, isDraft)
|
||||||
|
return args.Get(0).([]utils.ShallowDBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestPeer() *peer.Peer {
|
||||||
|
return &peer.Peer{
|
||||||
|
Url: "http://localhost",
|
||||||
|
WalletAddress: "0x123",
|
||||||
|
PublicKey: "pubkey",
|
||||||
|
State: peer.SELF,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteOne_UsingMock(t *testing.T) {
|
||||||
|
mockAcc := new(MockAccessor)
|
||||||
|
mockAcc.On("DeleteOne", "id").Return(newTestPeer(), 200, nil)
|
||||||
|
|
||||||
|
obj, code, err := mockAcc.DeleteOne("id")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.NotNil(t, obj)
|
||||||
|
mockAcc.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateOne_UsingMock(t *testing.T) {
|
||||||
|
mockAcc := new(MockAccessor)
|
||||||
|
peerObj := newTestPeer()
|
||||||
|
mockAcc.On("UpdateOne", peerObj, "id").Return(peerObj, 200, nil)
|
||||||
|
|
||||||
|
obj, code, err := mockAcc.UpdateOne(peerObj, "id")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.Equal(t, peerObj, obj)
|
||||||
|
mockAcc.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreOne_UsingMock(t *testing.T) {
|
||||||
|
mockAcc := new(MockAccessor)
|
||||||
|
peerObj := newTestPeer()
|
||||||
|
mockAcc.On("StoreOne", peerObj).Return(peerObj, 200, nil)
|
||||||
|
|
||||||
|
obj, code, err := mockAcc.StoreOne(peerObj)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.Equal(t, peerObj, obj)
|
||||||
|
mockAcc.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadOne_UsingMock(t *testing.T) {
|
||||||
|
mockAcc := new(MockAccessor)
|
||||||
|
mockAcc.On("LoadOne", "test-id").Return(newTestPeer(), 200, nil)
|
||||||
|
|
||||||
|
obj, code, err := mockAcc.LoadOne("test-id")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.NotNil(t, obj)
|
||||||
|
mockAcc.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadAll_UsingMock(t *testing.T) {
|
||||||
|
mockAcc := new(MockAccessor)
|
||||||
|
expected := []utils.ShallowDBObject{newTestPeer()}
|
||||||
|
mockAcc.On("LoadAll", false).Return(expected, 200, nil)
|
||||||
|
|
||||||
|
objs, code, err := mockAcc.LoadAll(false)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.Equal(t, expected, objs)
|
||||||
|
mockAcc.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearch_UsingMock(t *testing.T) {
|
||||||
|
mockAcc := new(MockAccessor)
|
||||||
|
filters := &dbs.Filters{}
|
||||||
|
expected := []utils.ShallowDBObject{newTestPeer()}
|
||||||
|
mockAcc.On("Search", filters, "test", false).Return(expected, 200, nil)
|
||||||
|
|
||||||
|
objs, code, err := mockAcc.Search(filters, "test", false)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.Equal(t, expected, objs)
|
||||||
|
mockAcc.AssertExpectations(t)
|
||||||
|
}
|
0
models/resources/compute.go
Normal file → Executable file
0
models/resources/compute.go
Normal file → Executable file
0
models/resources/data.go
Normal file → Executable file
0
models/resources/data.go
Normal file → Executable file
0
models/resources/interfaces.go
Normal file → Executable file
0
models/resources/interfaces.go
Normal file → Executable file
0
models/resources/models.go
Normal file → Executable file
0
models/resources/models.go
Normal file → Executable file
0
models/resources/priced_resource.go
Normal file → Executable file
0
models/resources/priced_resource.go
Normal file → Executable file
0
models/resources/processing.go
Normal file → Executable file
0
models/resources/processing.go
Normal file → Executable file
0
models/resources/resource.go
Normal file → Executable file
0
models/resources/resource.go
Normal file → Executable file
0
models/resources/resource_accessor.go
Normal file → Executable file
0
models/resources/resource_accessor.go
Normal file → Executable file
0
models/resources/storage.go
Normal file → Executable file
0
models/resources/storage.go
Normal file → Executable file
0
models/resources/workflow.go
Normal file → Executable file
0
models/resources/workflow.go
Normal file → Executable file
38
models/tests/models_test.go
Normal file
38
models/tests/models_test.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestModel_ReturnsValidInstances(t *testing.T) {
|
||||||
|
for name, _ := range models.ModelsCatalog {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
modelInt, _ := strconv.Atoi(name)
|
||||||
|
obj := models.Model(modelInt)
|
||||||
|
assert.NotNil(t, obj, "Model() returned nil for valid model name %s", name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModel_UnknownModelReturnsNil(t *testing.T) {
|
||||||
|
invalidModelInt := -9999 // unlikely to be valid
|
||||||
|
obj := models.Model(invalidModelInt)
|
||||||
|
assert.Nil(t, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetModelsNames_ReturnsAllKeys(t *testing.T) {
|
||||||
|
names := models.GetModelsNames()
|
||||||
|
assert.Len(t, names, len(models.ModelsCatalog))
|
||||||
|
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
for _, name := range names {
|
||||||
|
seen[name] = true
|
||||||
|
}
|
||||||
|
for key := range models.ModelsCatalog {
|
||||||
|
assert.Contains(t, seen, key)
|
||||||
|
}
|
||||||
|
}
|
0
models/utils/abstracts.go
Normal file → Executable file
0
models/utils/abstracts.go
Normal file → Executable file
0
models/utils/common.go
Normal file → Executable file
0
models/utils/common.go
Normal file → Executable file
0
models/utils/interfaces.go
Normal file → Executable file
0
models/utils/interfaces.go
Normal file → Executable file
128
models/utils/tests/abstracts_test.go
Normal file
128
models/utils/tests/abstracts_test.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package models_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.o-forge.io/core/oc-lib/dbs"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/utils"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/tools"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGenerateID(t *testing.T) {
|
||||||
|
ao := &utils.AbstractObject{}
|
||||||
|
ao.GenerateID()
|
||||||
|
assert.NotEmpty(t, ao.UUID)
|
||||||
|
_, err := uuid.Parse(ao.UUID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreDraftDefault(t *testing.T) {
|
||||||
|
ao := &utils.AbstractObject{IsDraft: true}
|
||||||
|
ao.StoreDraftDefault()
|
||||||
|
assert.False(t, ao.IsDraft)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCanUpdate(t *testing.T) {
|
||||||
|
ao := &utils.AbstractObject{}
|
||||||
|
res, set := ao.CanUpdate(nil)
|
||||||
|
assert.True(t, res)
|
||||||
|
assert.Nil(t, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCanDelete(t *testing.T) {
|
||||||
|
ao := &utils.AbstractObject{}
|
||||||
|
assert.True(t, ao.CanDelete())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsDrafted(t *testing.T) {
|
||||||
|
ao := &utils.AbstractObject{IsDraft: true}
|
||||||
|
assert.True(t, ao.IsDrafted())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetID(t *testing.T) {
|
||||||
|
u := uuid.New().String()
|
||||||
|
ao := &utils.AbstractObject{UUID: u}
|
||||||
|
assert.Equal(t, u, ao.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetName(t *testing.T) {
|
||||||
|
name := "MyObject"
|
||||||
|
ao := &utils.AbstractObject{Name: name}
|
||||||
|
assert.Equal(t, name, ao.GetName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetCreatorID(t *testing.T) {
|
||||||
|
id := "creator-123"
|
||||||
|
ao := &utils.AbstractObject{CreatorID: id}
|
||||||
|
assert.Equal(t, id, ao.GetCreatorID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpToDate_CreateFalse(t *testing.T) {
|
||||||
|
ao := &utils.AbstractObject{}
|
||||||
|
now := time.Now()
|
||||||
|
time.Sleep(time.Millisecond) // ensure time difference
|
||||||
|
ao.UpToDate("user123", "peer456", false)
|
||||||
|
assert.WithinDuration(t, now, ao.UpdateDate, time.Second)
|
||||||
|
assert.Equal(t, "peer456", ao.UpdaterID)
|
||||||
|
assert.Equal(t, "user123", ao.UserUpdaterID)
|
||||||
|
assert.True(t, ao.CreationDate.IsZero())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpToDate_CreateTrue(t *testing.T) {
|
||||||
|
ao := &utils.AbstractObject{}
|
||||||
|
now := time.Now()
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
ao.UpToDate("user123", "peer456", true)
|
||||||
|
assert.WithinDuration(t, now, ao.UpdateDate, time.Second)
|
||||||
|
assert.WithinDuration(t, now, ao.CreationDate, time.Second)
|
||||||
|
assert.Equal(t, "peer456", ao.UpdaterID)
|
||||||
|
assert.Equal(t, "peer456", ao.CreatorID)
|
||||||
|
assert.Equal(t, "user123", ao.UserUpdaterID)
|
||||||
|
assert.Equal(t, "user123", ao.UserCreatorID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyAuth(t *testing.T) {
|
||||||
|
request := &tools.APIRequest{PeerID: "peer123"}
|
||||||
|
ao := &utils.AbstractObject{CreatorID: "peer123"}
|
||||||
|
assert.True(t, ao.VerifyAuth(request))
|
||||||
|
|
||||||
|
ao = &utils.AbstractObject{AccessMode: utils.Public}
|
||||||
|
assert.True(t, ao.VerifyAuth(nil))
|
||||||
|
|
||||||
|
ao = &utils.AbstractObject{AccessMode: utils.Private, CreatorID: "peer123"}
|
||||||
|
request = &tools.APIRequest{PeerID: "wrong"}
|
||||||
|
assert.False(t, ao.VerifyAuth(request))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetObjectFilters(t *testing.T) {
|
||||||
|
ao := &utils.AbstractObject{}
|
||||||
|
f := ao.GetObjectFilters("*")
|
||||||
|
assert.NotNil(t, f)
|
||||||
|
assert.Contains(t, f.Or, "abstractobject.name")
|
||||||
|
assert.Equal(t, dbs.LIKE.String(), f.Or["abstractobject.name"][0].Operator)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeserialize(t *testing.T) {
|
||||||
|
ao := &utils.AbstractObject{}
|
||||||
|
input := map[string]interface{}{"name": "test", "id": uuid.New().String()}
|
||||||
|
res := ao.Deserialize(input, &utils.AbstractObject{})
|
||||||
|
assert.NotNil(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerialize(t *testing.T) {
|
||||||
|
ao := &utils.AbstractObject{Name: "test", UUID: uuid.New().String()}
|
||||||
|
m := ao.Serialize(ao)
|
||||||
|
assert.Equal(t, "test", m["name"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAbstractAccessorMethods(t *testing.T) {
|
||||||
|
r := &utils.AbstractAccessor{Request: &tools.APIRequest{Username: "alice", PeerID: "peer1", Groups: []string{"dev"}}}
|
||||||
|
assert.True(t, r.ShouldVerifyAuth())
|
||||||
|
assert.Equal(t, "alice", r.GetUser())
|
||||||
|
assert.Equal(t, "peer1", r.GetPeerID())
|
||||||
|
assert.Equal(t, []string{"dev"}, r.GetGroups())
|
||||||
|
assert.Equal(t, r.Request.Caller, r.GetCaller())
|
||||||
|
}
|
168
models/utils/tests/common_test.go
Normal file
168
models/utils/tests/common_test.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package models_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"cloud.o-forge.io/core/oc-lib/dbs"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/utils"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/tools"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- Mock Definitions ---
|
||||||
|
|
||||||
|
type MockDBObject struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) GetLogger() *zerolog.Logger {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *MockAccessor) GetGroups() []string {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) GetCaller() *tools.HTTPCaller {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockDBObject) GenerateID() { m.Called() }
|
||||||
|
func (m *MockDBObject) StoreDraftDefault() { m.Called() }
|
||||||
|
func (m *MockDBObject) UpToDate(user, peer string, create bool) {
|
||||||
|
m.Called(user, peer, create)
|
||||||
|
}
|
||||||
|
func (m *MockDBObject) VerifyAuth(req *tools.APIRequest) bool {
|
||||||
|
args := m.Called(req)
|
||||||
|
return args.Bool(0)
|
||||||
|
}
|
||||||
|
func (m *MockDBObject) CanDelete() bool {
|
||||||
|
args := m.Called()
|
||||||
|
return args.Bool(0)
|
||||||
|
}
|
||||||
|
func (m *MockDBObject) CanUpdate(set utils.DBObject) (bool, utils.DBObject) {
|
||||||
|
args := m.Called(set)
|
||||||
|
return args.Bool(0), args.Get(1).(utils.DBObject)
|
||||||
|
}
|
||||||
|
func (m *MockDBObject) IsDrafted() bool {
|
||||||
|
args := m.Called()
|
||||||
|
return args.Bool(0)
|
||||||
|
}
|
||||||
|
func (m *MockDBObject) Serialize(obj utils.DBObject) map[string]interface{} {
|
||||||
|
args := m.Called(obj)
|
||||||
|
return args.Get(0).(map[string]interface{})
|
||||||
|
}
|
||||||
|
func (m *MockDBObject) Deserialize(mdata map[string]interface{}, obj utils.DBObject) utils.DBObject {
|
||||||
|
args := m.Called(mdata, obj)
|
||||||
|
return args.Get(0).(utils.DBObject)
|
||||||
|
}
|
||||||
|
func (m *MockDBObject) GetID() string {
|
||||||
|
args := m.Called()
|
||||||
|
return args.String(0)
|
||||||
|
}
|
||||||
|
func (m *MockDBObject) GetName() string {
|
||||||
|
args := m.Called()
|
||||||
|
return args.String(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockAccessor struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) DeleteOne(id string) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(id)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
func (m *MockAccessor) LoadOne(id string) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(id)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) LoadAll(isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
||||||
|
args := m.Called(isDraft)
|
||||||
|
return args.Get(0).([]utils.ShallowDBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) UpdateOne(set utils.DBObject, id string) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(set, id)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) CopyOne(data utils.DBObject) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(data)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) StoreOne(data utils.DBObject) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(data)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) ShouldVerifyAuth() bool {
|
||||||
|
args := m.Called()
|
||||||
|
return args.Bool(0)
|
||||||
|
}
|
||||||
|
func (m *MockAccessor) GetRequest() *tools.APIRequest {
|
||||||
|
args := m.Called()
|
||||||
|
return args.Get(0).(*tools.APIRequest)
|
||||||
|
}
|
||||||
|
func (m *MockAccessor) GetType() tools.DataType {
|
||||||
|
args := m.Called()
|
||||||
|
return args.Get(0).(tools.DataType)
|
||||||
|
}
|
||||||
|
func (m *MockAccessor) Search(filters *dbs.Filters, s string, d bool) ([]utils.ShallowDBObject, int, error) {
|
||||||
|
args := m.Called(filters, s, d)
|
||||||
|
return args.Get(0).([]utils.ShallowDBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) GetUser() string {
|
||||||
|
args := m.Called()
|
||||||
|
return args.String(0)
|
||||||
|
}
|
||||||
|
func (m *MockAccessor) GetPeerID() string {
|
||||||
|
args := m.Called()
|
||||||
|
return args.String(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Test Cases ---
|
||||||
|
|
||||||
|
func TestVerifyAccess_Authorized(t *testing.T) {
|
||||||
|
mockObj := new(MockDBObject)
|
||||||
|
mockAcc := new(MockAccessor)
|
||||||
|
|
||||||
|
req := &tools.APIRequest{PeerID: "peer"}
|
||||||
|
mockAcc.On("LoadOne", "123").Return(mockObj, 200, nil)
|
||||||
|
mockAcc.On("ShouldVerifyAuth").Return(true)
|
||||||
|
mockObj.On("VerifyAuth", req).Return(true)
|
||||||
|
mockAcc.On("GetRequest").Return(req)
|
||||||
|
|
||||||
|
err := utils.VerifyAccess(mockAcc, "123")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyAccess_Unauthorized(t *testing.T) {
|
||||||
|
mockObj := new(MockDBObject)
|
||||||
|
mockAcc := new(MockAccessor)
|
||||||
|
|
||||||
|
req := &tools.APIRequest{PeerID: "peer"}
|
||||||
|
mockAcc.On("LoadOne", "123").Return(mockObj, 200, nil)
|
||||||
|
mockAcc.On("ShouldVerifyAuth").Return(true)
|
||||||
|
mockObj.On("VerifyAuth", req).Return(false)
|
||||||
|
mockAcc.On("GetRequest").Return(req)
|
||||||
|
|
||||||
|
err := utils.VerifyAccess(mockAcc, "123")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyAccess_LoadError(t *testing.T) {
|
||||||
|
mockAcc := new(MockAccessor)
|
||||||
|
|
||||||
|
mockAcc.On("LoadOne", "bad-id").Return(nil, 404, errors.New("not found"))
|
||||||
|
|
||||||
|
err := utils.VerifyAccess(mockAcc, "bad-id")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, "not found", err.Error())
|
||||||
|
}
|
149
models/workflow_execution/tests/workflow_scheduler_test.go
Normal file
149
models/workflow_execution/tests/workflow_scheduler_test.go
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package workflow_execution_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/common/enum"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/utils"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/workflow"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/workflow_execution"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockAccessor struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) LoadOne(id string) (interface{}, int, error) {
|
||||||
|
args := m.Called(id)
|
||||||
|
return args.Get(0), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewScheduler_ValidInput(t *testing.T) {
|
||||||
|
s := "2025-06-16T15:00:00"
|
||||||
|
e := "2025-06-16T17:00:00"
|
||||||
|
dur := 7200.0
|
||||||
|
cronStr := "0 0 * * * *"
|
||||||
|
|
||||||
|
sched := workflow_execution.NewScheduler(s, e, dur, cronStr)
|
||||||
|
|
||||||
|
assert.NotNil(t, sched)
|
||||||
|
assert.Equal(t, dur, sched.DurationS)
|
||||||
|
assert.Equal(t, cronStr, sched.Cron)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewScheduler_InvalidStart(t *testing.T) {
|
||||||
|
s := "invalid"
|
||||||
|
e := "2025-06-16T17:00:00"
|
||||||
|
dur := 7200.0
|
||||||
|
cronStr := "0 0 * * * *"
|
||||||
|
|
||||||
|
sched := workflow_execution.NewScheduler(s, e, dur, cronStr)
|
||||||
|
assert.Nil(t, sched)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewScheduler_InvalidEnd(t *testing.T) {
|
||||||
|
s := "2025-06-16T15:00:00"
|
||||||
|
e := "invalid"
|
||||||
|
dur := 7200.0
|
||||||
|
cronStr := "0 0 * * * *"
|
||||||
|
|
||||||
|
sched := workflow_execution.NewScheduler(s, e, dur, cronStr)
|
||||||
|
assert.NotNil(t, sched)
|
||||||
|
assert.Nil(t, sched.End)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDates_NoCron(t *testing.T) {
|
||||||
|
start := time.Now()
|
||||||
|
end := start.Add(2 * time.Hour)
|
||||||
|
|
||||||
|
s := &workflow_execution.WorkflowSchedule{
|
||||||
|
Start: start,
|
||||||
|
End: &end,
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule, err := s.GetDates()
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, schedule, 1)
|
||||||
|
assert.Equal(t, start, schedule[0].Start)
|
||||||
|
assert.Equal(t, end, *schedule[0].End)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDates_InvalidCron(t *testing.T) {
|
||||||
|
start := time.Now()
|
||||||
|
end := start.Add(2 * time.Hour)
|
||||||
|
|
||||||
|
s := &workflow_execution.WorkflowSchedule{
|
||||||
|
Start: start,
|
||||||
|
End: &end,
|
||||||
|
Cron: "bad cron",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := s.GetDates()
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDates_ValidCron(t *testing.T) {
|
||||||
|
start := time.Now()
|
||||||
|
end := start.Add(10 * time.Minute)
|
||||||
|
|
||||||
|
s := &workflow_execution.WorkflowSchedule{
|
||||||
|
Start: start,
|
||||||
|
End: &end,
|
||||||
|
DurationS: 60,
|
||||||
|
Cron: "0 */2 * * * *",
|
||||||
|
}
|
||||||
|
|
||||||
|
dates, err := s.GetDates()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Greater(t, len(dates), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetExecutions_Success(t *testing.T) {
|
||||||
|
start := time.Now()
|
||||||
|
end := start.Add(1 * time.Hour)
|
||||||
|
ws := &workflow_execution.WorkflowSchedule{
|
||||||
|
UUID: uuid.New().String(),
|
||||||
|
Start: start,
|
||||||
|
End: &end,
|
||||||
|
}
|
||||||
|
|
||||||
|
wf := &workflow.Workflow{
|
||||||
|
AbstractObject: utils.AbstractObject{
|
||||||
|
UUID: uuid.New().String(),
|
||||||
|
Name: "TestWorkflow",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
execs, err := ws.GetExecutions(wf)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Greater(t, len(execs), 0)
|
||||||
|
assert.Equal(t, wf.UUID, execs[0].WorkflowID)
|
||||||
|
assert.Equal(t, ws.UUID, execs[0].ExecutionsID)
|
||||||
|
assert.Equal(t, enum.DRAFT, execs[0].State)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSchedules_NoRequest(t *testing.T) {
|
||||||
|
ws := &workflow_execution.WorkflowSchedule{}
|
||||||
|
|
||||||
|
ws, wf, execs, err := ws.Schedules("someID", nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, wf)
|
||||||
|
assert.Len(t, execs, 0)
|
||||||
|
assert.Equal(t, ws, ws)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional test stubs to be completed with gomock usage for:
|
||||||
|
// - CheckBooking
|
||||||
|
// - BookExecs
|
||||||
|
// - getBooking
|
||||||
|
// - Schedules (success path)
|
||||||
|
// - Planify mocking in CheckBooking
|
||||||
|
// - Peer interaction in BookExecs
|
||||||
|
// - Caller deep copy errors in getCallerCopy
|
||||||
|
// Will be continued...
|
154
models/workflow_execution/tests/workflow_test.go
Executable file
154
models/workflow_execution/tests/workflow_test.go
Executable file
@ -0,0 +1,154 @@
|
|||||||
|
package workflow_execution_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/common/enum"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/utils"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/workflow_execution"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/tools"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *MockAccessor) DeleteOne(id string) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(id)
|
||||||
|
return nil, args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAccessor) Search(filters interface{}, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
||||||
|
args := m.Called(filters, search, isDraft)
|
||||||
|
return args.Get(0).([]utils.ShallowDBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreDraftDefault(t *testing.T) {
|
||||||
|
exec := &workflow_execution.WorkflowExecution{}
|
||||||
|
exec.StoreDraftDefault()
|
||||||
|
assert.False(t, exec.IsDraft)
|
||||||
|
assert.Equal(t, enum.SCHEDULED, exec.State)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCanUpdate_StateChange(t *testing.T) {
|
||||||
|
existing := &workflow_execution.WorkflowExecution{State: enum.DRAFT}
|
||||||
|
newExec := &workflow_execution.WorkflowExecution{State: enum.SCHEDULED}
|
||||||
|
canUpdate, updated := existing.CanUpdate(newExec)
|
||||||
|
assert.True(t, canUpdate)
|
||||||
|
assert.Equal(t, enum.SCHEDULED, updated.(*workflow_execution.WorkflowExecution).State)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCanUpdate_SameState_Draft(t *testing.T) {
|
||||||
|
existing := &workflow_execution.WorkflowExecution{AbstractObject: utils.AbstractObject{IsDraft: true}, State: enum.DRAFT}
|
||||||
|
newExec := &workflow_execution.WorkflowExecution{AbstractObject: utils.AbstractObject{IsDraft: true}, State: enum.DRAFT}
|
||||||
|
canUpdate, _ := existing.CanUpdate(newExec)
|
||||||
|
assert.False(t, canUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCanDelete_TrueIfDraft(t *testing.T) {
|
||||||
|
exec := &workflow_execution.WorkflowExecution{AbstractObject: utils.AbstractObject{IsDraft: true}}
|
||||||
|
assert.True(t, exec.CanDelete())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCanDelete_FalseIfNotDraft(t *testing.T) {
|
||||||
|
exec := &workflow_execution.WorkflowExecution{AbstractObject: utils.AbstractObject{IsDraft: false}}
|
||||||
|
assert.False(t, exec.CanDelete())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEquals_True(t *testing.T) {
|
||||||
|
d := time.Now()
|
||||||
|
exec1 := &workflow_execution.WorkflowExecution{ExecDate: d, WorkflowID: "123"}
|
||||||
|
exec2 := &workflow_execution.WorkflowExecution{ExecDate: d, WorkflowID: "123"}
|
||||||
|
assert.True(t, exec1.Equals(exec2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEquals_False(t *testing.T) {
|
||||||
|
exec1 := &workflow_execution.WorkflowExecution{ExecDate: time.Now(), WorkflowID: "abc"}
|
||||||
|
exec2 := &workflow_execution.WorkflowExecution{ExecDate: time.Now().Add(time.Hour), WorkflowID: "def"}
|
||||||
|
assert.False(t, exec1.Equals(exec2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArgoStatusToState_Success(t *testing.T) {
|
||||||
|
exec := &workflow_execution.WorkflowExecution{}
|
||||||
|
exec.ArgoStatusToState("succeeded")
|
||||||
|
assert.Equal(t, enum.SUCCESS, exec.State)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArgoStatusToState_DefaultToFailure(t *testing.T) {
|
||||||
|
exec := &workflow_execution.WorkflowExecution{}
|
||||||
|
exec.ArgoStatusToState("unknown")
|
||||||
|
assert.Equal(t, enum.FAILURE, exec.State)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateID_AssignsUUID(t *testing.T) {
|
||||||
|
exec := &workflow_execution.WorkflowExecution{}
|
||||||
|
exec.GenerateID()
|
||||||
|
assert.NotEmpty(t, exec.UUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetName_ReturnsCorrectFormat(t *testing.T) {
|
||||||
|
time := time.Now()
|
||||||
|
exec := &workflow_execution.WorkflowExecution{AbstractObject: utils.AbstractObject{UUID: "abc"}, ExecDate: time}
|
||||||
|
assert.Contains(t, exec.GetName(), "abc")
|
||||||
|
assert.Contains(t, exec.GetName(), time.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyAuth_AlwaysTrue(t *testing.T) {
|
||||||
|
exec := &workflow_execution.WorkflowExecution{}
|
||||||
|
assert.True(t, exec.VerifyAuth(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateOne_RejectsZeroState(t *testing.T) {
|
||||||
|
accessor := &workflow_execution.WorkflowExecutionMongoAccessor{}
|
||||||
|
set := &workflow_execution.WorkflowExecution{State: 0}
|
||||||
|
_, code, err := accessor.UpdateOne(set, "someID")
|
||||||
|
assert.Equal(t, 400, code)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadOne_DraftExpired_ShouldDelete(t *testing.T) {
|
||||||
|
// Normally would mock time.Now and delete call; for now we test structure
|
||||||
|
accessor := workflow_execution.NewAccessor(&tools.APIRequest{})
|
||||||
|
exec := &workflow_execution.WorkflowExecution{
|
||||||
|
ExecDate: time.Now().Add(-2 * time.Minute),
|
||||||
|
State: enum.DRAFT,
|
||||||
|
AbstractObject: utils.AbstractObject{UUID: "to-delete"},
|
||||||
|
}
|
||||||
|
_, _, _ = accessor.LoadOne(exec.GetID())
|
||||||
|
// No panic = good enough placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadOne_ScheduledExpired_ShouldUpdateToForgotten(t *testing.T) {
|
||||||
|
accessor := workflow_execution.NewAccessor(&tools.APIRequest{})
|
||||||
|
exec := &workflow_execution.WorkflowExecution{
|
||||||
|
ExecDate: time.Now().Add(-2 * time.Minute),
|
||||||
|
State: enum.SCHEDULED,
|
||||||
|
AbstractObject: utils.AbstractObject{UUID: "to-forget"},
|
||||||
|
}
|
||||||
|
_, _, _ = accessor.LoadOne(exec.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteOne_NotImplemented(t *testing.T) {
|
||||||
|
accessor := workflow_execution.NewAccessor(&tools.APIRequest{})
|
||||||
|
_, code, err := accessor.DeleteOne("someID")
|
||||||
|
assert.Equal(t, 404, code)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreOne_NotImplemented(t *testing.T) {
|
||||||
|
accessor := workflow_execution.NewAccessor(&tools.APIRequest{})
|
||||||
|
_, code, err := accessor.StoreOne(nil)
|
||||||
|
assert.Equal(t, 404, code)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyOne_NotImplemented(t *testing.T) {
|
||||||
|
accessor := workflow_execution.NewAccessor(&tools.APIRequest{})
|
||||||
|
_, code, err := accessor.CopyOne(nil)
|
||||||
|
assert.Equal(t, 404, code)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetExecFilters_BasicPattern(t *testing.T) {
|
||||||
|
a := workflow_execution.NewAccessor(&tools.APIRequest{})
|
||||||
|
filters := a.GetExecFilters("foo")
|
||||||
|
assert.Contains(t, filters.Or["abstractobject.name"][0].Value, "foo")
|
||||||
|
}
|
0
models/workflow_execution/workflow_execution.go
Normal file → Executable file
0
models/workflow_execution/workflow_execution.go
Normal file → Executable file
28
models/workflow_execution/workflow_execution_mongo_accessor.go
Normal file → Executable file
28
models/workflow_execution/workflow_execution_mongo_accessor.go
Normal file → Executable file
@ -11,13 +11,13 @@ import (
|
|||||||
"cloud.o-forge.io/core/oc-lib/tools"
|
"cloud.o-forge.io/core/oc-lib/tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
type workflowExecutionMongoAccessor struct {
|
type WorkflowExecutionMongoAccessor struct {
|
||||||
utils.AbstractAccessor
|
utils.AbstractAccessor
|
||||||
shallow bool
|
shallow bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newShallowAccessor(request *tools.APIRequest) *workflowExecutionMongoAccessor {
|
func newShallowAccessor(request *tools.APIRequest) *WorkflowExecutionMongoAccessor {
|
||||||
return &workflowExecutionMongoAccessor{
|
return &WorkflowExecutionMongoAccessor{
|
||||||
shallow: true,
|
shallow: true,
|
||||||
AbstractAccessor: utils.AbstractAccessor{
|
AbstractAccessor: utils.AbstractAccessor{
|
||||||
Logger: logs.CreateLogger(tools.WORKFLOW_EXECUTION.String()), // Create a logger with the data type
|
Logger: logs.CreateLogger(tools.WORKFLOW_EXECUTION.String()), // Create a logger with the data type
|
||||||
@ -27,8 +27,8 @@ func newShallowAccessor(request *tools.APIRequest) *workflowExecutionMongoAccess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAccessor(request *tools.APIRequest) *workflowExecutionMongoAccessor {
|
func NewAccessor(request *tools.APIRequest) *WorkflowExecutionMongoAccessor {
|
||||||
return &workflowExecutionMongoAccessor{
|
return &WorkflowExecutionMongoAccessor{
|
||||||
shallow: false,
|
shallow: false,
|
||||||
AbstractAccessor: utils.AbstractAccessor{
|
AbstractAccessor: utils.AbstractAccessor{
|
||||||
Logger: logs.CreateLogger(tools.WORKFLOW_EXECUTION.String()), // Create a logger with the data type
|
Logger: logs.CreateLogger(tools.WORKFLOW_EXECUTION.String()), // Create a logger with the data type
|
||||||
@ -38,11 +38,11 @@ func NewAccessor(request *tools.APIRequest) *workflowExecutionMongoAccessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wfa *workflowExecutionMongoAccessor) DeleteOne(id string) (utils.DBObject, int, error) {
|
func (wfa *WorkflowExecutionMongoAccessor) DeleteOne(id string) (utils.DBObject, int, error) {
|
||||||
return nil, 404, errors.New("not implemented")
|
return nil, 404, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wfa *workflowExecutionMongoAccessor) UpdateOne(set utils.DBObject, id string) (utils.DBObject, int, error) {
|
func (wfa *WorkflowExecutionMongoAccessor) UpdateOne(set utils.DBObject, id string) (utils.DBObject, int, error) {
|
||||||
if set.(*WorkflowExecution).State == 0 {
|
if set.(*WorkflowExecution).State == 0 {
|
||||||
return nil, 400, errors.New("state is required")
|
return nil, 400, errors.New("state is required")
|
||||||
}
|
}
|
||||||
@ -50,15 +50,15 @@ func (wfa *workflowExecutionMongoAccessor) UpdateOne(set utils.DBObject, id stri
|
|||||||
return utils.GenericUpdateOne(&realSet, id, wfa, &WorkflowExecution{})
|
return utils.GenericUpdateOne(&realSet, id, wfa, &WorkflowExecution{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wfa *workflowExecutionMongoAccessor) StoreOne(data utils.DBObject) (utils.DBObject, int, error) {
|
func (wfa *WorkflowExecutionMongoAccessor) StoreOne(data utils.DBObject) (utils.DBObject, int, error) {
|
||||||
return nil, 404, errors.New("not implemented")
|
return nil, 404, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wfa *workflowExecutionMongoAccessor) CopyOne(data utils.DBObject) (utils.DBObject, int, error) {
|
func (wfa *WorkflowExecutionMongoAccessor) CopyOne(data utils.DBObject) (utils.DBObject, int, error) {
|
||||||
return nil, 404, errors.New("not implemented")
|
return nil, 404, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *workflowExecutionMongoAccessor) LoadOne(id string) (utils.DBObject, int, error) {
|
func (a *WorkflowExecutionMongoAccessor) LoadOne(id string) (utils.DBObject, int, error) {
|
||||||
return utils.GenericLoadOne[*WorkflowExecution](id, func(d utils.DBObject) (utils.DBObject, int, error) {
|
return utils.GenericLoadOne[*WorkflowExecution](id, func(d utils.DBObject) (utils.DBObject, int, error) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
now = now.Add(time.Second * -60)
|
now = now.Add(time.Second * -60)
|
||||||
@ -74,15 +74,15 @@ func (a *workflowExecutionMongoAccessor) LoadOne(id string) (utils.DBObject, int
|
|||||||
}, a)
|
}, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *workflowExecutionMongoAccessor) LoadAll(isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
func (a *WorkflowExecutionMongoAccessor) LoadAll(isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
||||||
return utils.GenericLoadAll[*WorkflowExecution](a.getExec(), isDraft, a)
|
return utils.GenericLoadAll[*WorkflowExecution](a.getExec(), isDraft, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *workflowExecutionMongoAccessor) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
func (a *WorkflowExecutionMongoAccessor) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
||||||
return utils.GenericSearch[*WorkflowExecution](filters, search, a.GetExecFilters(search), a.getExec(), isDraft, a)
|
return utils.GenericSearch[*WorkflowExecution](filters, search, a.GetExecFilters(search), a.getExec(), isDraft, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *workflowExecutionMongoAccessor) getExec() func(utils.DBObject) utils.ShallowDBObject {
|
func (a *WorkflowExecutionMongoAccessor) getExec() func(utils.DBObject) utils.ShallowDBObject {
|
||||||
return func(d utils.DBObject) utils.ShallowDBObject {
|
return func(d utils.DBObject) utils.ShallowDBObject {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
now = now.Add(time.Second * -60)
|
now = now.Add(time.Second * -60)
|
||||||
@ -99,7 +99,7 @@ func (a *workflowExecutionMongoAccessor) getExec() func(utils.DBObject) utils.Sh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *workflowExecutionMongoAccessor) GetExecFilters(search string) *dbs.Filters {
|
func (a *WorkflowExecutionMongoAccessor) GetExecFilters(search string) *dbs.Filters {
|
||||||
return &dbs.Filters{
|
return &dbs.Filters{
|
||||||
Or: map[string][]dbs.Filter{ // filter by name if no filters are provided
|
Or: map[string][]dbs.Filter{ // filter by name if no filters are provided
|
||||||
"abstractobject.name": {{Operator: dbs.LIKE.String(), Value: search + "_execution"}},
|
"abstractobject.name": {{Operator: dbs.LIKE.String(), Value: search + "_execution"}},
|
||||||
|
12
models/workflow_execution/workflow_scheduler.go
Normal file → Executable file
12
models/workflow_execution/workflow_scheduler.go
Normal file → Executable file
@ -71,7 +71,7 @@ func (ws *WorkflowSchedule) CheckBooking(wfID string, request *tools.APIRequest)
|
|||||||
if ws.End != nil && ws.Start.Add(time.Duration(longest)*time.Second).After(*ws.End) {
|
if ws.End != nil && ws.Start.Add(time.Duration(longest)*time.Second).After(*ws.End) {
|
||||||
ws.Warning = "The workflow may be too long to be executed in the given time frame, we will try to book it anyway\n"
|
ws.Warning = "The workflow may be too long to be executed in the given time frame, we will try to book it anyway\n"
|
||||||
}
|
}
|
||||||
execs, err := ws.getExecutions(wf)
|
execs, err := ws.GetExecutions(wf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, wf, []*WorkflowExecution{}, []*booking.Booking{}, err
|
return false, wf, []*WorkflowExecution{}, []*booking.Booking{}, err
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ func (ws *WorkflowSchedule) CheckBooking(wfID string, request *tools.APIRequest)
|
|||||||
return true, wf, execs, bookings, nil
|
return true, wf, execs, bookings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBooking( b *booking.Booking, request *tools.APIRequest, wf *workflow.Workflow, execs []*WorkflowExecution, bookings []*booking.Booking, errCh chan error, m *sync.Mutex) {
|
func getBooking(b *booking.Booking, request *tools.APIRequest, wf *workflow.Workflow, execs []*WorkflowExecution, bookings []*booking.Booking, errCh chan error, m *sync.Mutex) {
|
||||||
|
|
||||||
m.Lock()
|
m.Lock()
|
||||||
c, err := getCallerCopy(request, errCh)
|
c, err := getCallerCopy(request, errCh)
|
||||||
@ -159,7 +159,7 @@ func (ws *WorkflowSchedule) Schedules(wfID string, request *tools.APIRequest) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(bookings); i++ {
|
for i := 0; i < len(bookings); i++ {
|
||||||
if err := <- errCh ; err != nil {
|
if err := <-errCh; err != nil {
|
||||||
return ws, wf, executions, errors.New("could not launch the peer execution : " + fmt.Sprintf("%v", err))
|
return ws, wf, executions, errors.New("could not launch the peer execution : " + fmt.Sprintf("%v", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,9 +210,9 @@ VERIFY THAT WE HANDLE DIFFERENCE BETWEEN LOCATION TIME && BOOKING
|
|||||||
* getExecutions is a function that returns the executions of a workflow
|
* getExecutions is a function that returns the executions of a workflow
|
||||||
* it returns an array of workflow_execution.WorkflowExecution
|
* it returns an array of workflow_execution.WorkflowExecution
|
||||||
*/
|
*/
|
||||||
func (ws *WorkflowSchedule) getExecutions(workflow *workflow.Workflow) ([]*WorkflowExecution, error) {
|
func (ws *WorkflowSchedule) GetExecutions(workflow *workflow.Workflow) ([]*WorkflowExecution, error) {
|
||||||
workflows_executions := []*WorkflowExecution{}
|
workflows_executions := []*WorkflowExecution{}
|
||||||
dates, err := ws.getDates()
|
dates, err := ws.GetDates()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return workflows_executions, err
|
return workflows_executions, err
|
||||||
}
|
}
|
||||||
@ -233,7 +233,7 @@ func (ws *WorkflowSchedule) getExecutions(workflow *workflow.Workflow) ([]*Workf
|
|||||||
return workflows_executions, nil
|
return workflows_executions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WorkflowSchedule) getDates() ([]Schedule, error) {
|
func (ws *WorkflowSchedule) GetDates() ([]Schedule, error) {
|
||||||
schedule := []Schedule{}
|
schedule := []Schedule{}
|
||||||
if len(ws.Cron) > 0 { // if cron is set then end date should be set
|
if len(ws.Cron) > 0 { // if cron is set then end date should be set
|
||||||
if ws.End == nil {
|
if ws.End == nil {
|
||||||
|
215
models/workspace/tests/workspace_mongo_accessor_test.go
Normal file
215
models/workspace/tests/workspace_mongo_accessor_test.go
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
// File: workspace_accessor_test.go
|
||||||
|
|
||||||
|
package workspace_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"cloud.o-forge.io/core/oc-lib/dbs"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/utils"
|
||||||
|
"cloud.o-forge.io/core/oc-lib/models/workspace"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockWorkspaceAccessor struct {
|
||||||
|
mock.Mock
|
||||||
|
workspace.Workspace
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockWorkspaceAccessor) StoreOne(data utils.DBObject) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(data)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockWorkspaceAccessor) UpdateOne(set utils.DBObject, id string) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(set, id)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockWorkspaceAccessor) DeleteOne(id string) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(id)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockWorkspaceAccessor) LoadOne(id string) (utils.DBObject, int, error) {
|
||||||
|
args := m.Called(id)
|
||||||
|
return args.Get(0).(utils.DBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockWorkspaceAccessor) LoadAll(isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
||||||
|
args := m.Called(isDraft)
|
||||||
|
return args.Get(0).([]utils.ShallowDBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockWorkspaceAccessor) Search(filters *dbs.Filters, search string, isDraft bool) ([]utils.ShallowDBObject, int, error) {
|
||||||
|
args := m.Called(filters, search, isDraft)
|
||||||
|
return args.Get(0).([]utils.ShallowDBObject), args.Int(1), args.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreOne_Success(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
ws := &workspace.Workspace{AbstractObject: utils.AbstractObject{Name: "test_ws"}}
|
||||||
|
mockAcc.On("StoreOne", ws).Return(ws, 200, nil)
|
||||||
|
|
||||||
|
res, code, err := mockAcc.StoreOne(ws)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.Equal(t, ws, res)
|
||||||
|
mockAcc.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreOne_Conflict(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
ws := &workspace.Workspace{AbstractObject: utils.AbstractObject{Name: "duplicate"}}
|
||||||
|
mockAcc.On("StoreOne", ws).Return(nil, 409, errors.New("a workspace with the same name already exists"))
|
||||||
|
|
||||||
|
res, code, err := mockAcc.StoreOne(ws)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, 409, code)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateOne_Success(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
ws := &workspace.Workspace{AbstractObject: utils.AbstractObject{UUID: "123", IsDraft: false}}
|
||||||
|
mockAcc.On("UpdateOne", ws, "123").Return(ws, 200, nil)
|
||||||
|
|
||||||
|
res, code, err := mockAcc.UpdateOne(ws, "123")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.Equal(t, ws, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateOne_Error(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
ws := &workspace.Workspace{AbstractObject: utils.AbstractObject{UUID: "999"}}
|
||||||
|
err := errors.New("update failed")
|
||||||
|
mockAcc.On("UpdateOne", ws, "999").Return(nil, 500, err)
|
||||||
|
|
||||||
|
res, code, err := mockAcc.UpdateOne(ws, "999")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, 500, code)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteOne_Success(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
ws := &workspace.Workspace{AbstractObject: utils.AbstractObject{UUID: "321"}}
|
||||||
|
mockAcc.On("DeleteOne", "321").Return(ws, 200, nil)
|
||||||
|
|
||||||
|
res, code, err := mockAcc.DeleteOne("321")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.Equal(t, ws, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteOne_NotFound(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
err := errors.New("not found")
|
||||||
|
mockAcc.On("DeleteOne", "notfound").Return(nil, 404, err)
|
||||||
|
|
||||||
|
res, code, err := mockAcc.DeleteOne("notfound")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, 404, code)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadOne_Success(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
ws := &workspace.Workspace{AbstractObject: utils.AbstractObject{UUID: "loadid"}}
|
||||||
|
mockAcc.On("LoadOne", "loadid").Return(ws, 200, nil)
|
||||||
|
|
||||||
|
res, code, err := mockAcc.LoadOne("loadid")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.Equal(t, ws, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadOne_Error(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
err := errors.New("db error")
|
||||||
|
mockAcc.On("LoadOne", "badid").Return(nil, 500, err)
|
||||||
|
|
||||||
|
res, code, err := mockAcc.LoadOne("badid")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, 500, code)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadAll_Success(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
ws := &workspace.Workspace{AbstractObject: utils.AbstractObject{UUID: "all1"}}
|
||||||
|
mockAcc.On("LoadAll", true).Return([]utils.ShallowDBObject{ws}, 200, nil)
|
||||||
|
|
||||||
|
res, code, err := mockAcc.LoadAll(true)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.Len(t, res, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadAll_Empty(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
mockAcc.On("LoadAll", false).Return([]utils.ShallowDBObject{}, 200, nil)
|
||||||
|
|
||||||
|
res, code, err := mockAcc.LoadAll(false)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.Empty(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearch_Success(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
filters := &dbs.Filters{}
|
||||||
|
mockAcc.On("Search", filters, "keyword", true).Return([]utils.ShallowDBObject{}, 200, nil)
|
||||||
|
|
||||||
|
res, code, err := mockAcc.Search(filters, "keyword", true)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 200, code)
|
||||||
|
assert.NotNil(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearch_Error(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
filters := &dbs.Filters{}
|
||||||
|
err := errors.New("search failed")
|
||||||
|
mockAcc.On("Search", filters, "fail", false).Return(nil, 500, err)
|
||||||
|
|
||||||
|
res, code, err := mockAcc.Search(filters, "fail", false)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, 500, code)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional edge test cases
|
||||||
|
|
||||||
|
func TestStoreOne_InvalidType(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
mockAcc.On("StoreOne", mock.Anything).Return(nil, 400, errors.New("invalid type"))
|
||||||
|
|
||||||
|
res, code, err := mockAcc.StoreOne(&utils.AbstractObject{})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, 400, code)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateOne_NilData(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
mockAcc.On("UpdateOne", nil, "id").Return(nil, 400, errors.New("nil data"))
|
||||||
|
|
||||||
|
res, code, err := mockAcc.UpdateOne(nil, "id")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, 400, code)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteOne_NilID(t *testing.T) {
|
||||||
|
mockAcc := new(MockWorkspaceAccessor)
|
||||||
|
mockAcc.On("DeleteOne", "").Return(nil, 400, errors.New("missing ID"))
|
||||||
|
|
||||||
|
res, code, err := mockAcc.DeleteOne("")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, 400, code)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
}
|
@ -47,12 +47,19 @@ func ToMethod(str string) METHOD {
|
|||||||
return GET
|
return GET
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HTTPCallerITF interface {
|
||||||
|
GetUrls() map[DataType]map[METHOD]string
|
||||||
|
CallGet(url string, subpath string, types ...string) ([]byte, error)
|
||||||
|
CallPost(url string, subpath string, body interface{}, types ...string) ([]byte, error)
|
||||||
|
CallDelete(url string, subpath string) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
var HTTPCallerInstance = &HTTPCaller{} // Singleton instance of the HTTPCaller
|
var HTTPCallerInstance = &HTTPCaller{} // Singleton instance of the HTTPCaller
|
||||||
|
|
||||||
type HTTPCaller struct {
|
type HTTPCaller struct {
|
||||||
URLS map[DataType]map[METHOD]string // Map of the different methods and their urls
|
URLS map[DataType]map[METHOD]string // Map of the different methods and their urls
|
||||||
Disabled bool // Disabled flag
|
Disabled bool // Disabled flag
|
||||||
LastResults map[string]interface{} // Used to store information regarding the last execution of a given method on a given data type
|
LastResults map[string]interface{} // Used to store information regarding the last execution of a given method on a given data type
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHTTPCaller creates a new instance of the HTTP Caller
|
// NewHTTPCaller creates a new instance of the HTTP Caller
|
||||||
@ -63,8 +70,12 @@ func NewHTTPCaller(urls map[DataType]map[METHOD]string) *HTTPCaller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *HTTPCaller) GetUrls() map[DataType]map[METHOD]string {
|
||||||
|
return c.URLS
|
||||||
|
}
|
||||||
|
|
||||||
// Creates a copy of the current caller, in order to have parallelized executions without race condition
|
// Creates a copy of the current caller, in order to have parallelized executions without race condition
|
||||||
func (c* HTTPCaller) DeepCopy(dst HTTPCaller) error {
|
func (c *HTTPCaller) DeepCopy(dst HTTPCaller) error {
|
||||||
bytes, err := json.Marshal(c)
|
bytes, err := json.Marshal(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
Loading…
Reference in New Issue
Block a user