From 3550b5e5e96b48f24a92b4cdd0a53ba3d9f4ffa6 Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Tue, 3 Nov 2020 22:24:29 -0800 Subject: [PATCH] add robot mgr the robot account manager to handle the CRUD Signed-off-by: wang yan --- .../postgresql/0050_2.2.0_schema.up.sql | 1 + src/pkg/robot2/dao/dao.go | 139 ++++++++++++++++ src/pkg/robot2/dao/dao_test.go | 156 ++++++++++++++++++ src/pkg/robot2/manager.go | 85 ++++++++++ src/pkg/robot2/manager_test.go | 76 +++++++++ src/pkg/robot2/model/model.go | 30 ++++ src/testing/pkg/pkg.go | 2 + src/testing/pkg/robot2/dao/dao.go | 155 +++++++++++++++++ src/testing/pkg/robot2/manager.go | 147 +++++++++++++++++ 9 files changed, 791 insertions(+) create mode 100644 src/pkg/robot2/dao/dao.go create mode 100644 src/pkg/robot2/dao/dao_test.go create mode 100644 src/pkg/robot2/manager.go create mode 100644 src/pkg/robot2/manager_test.go create mode 100644 src/pkg/robot2/model/model.go create mode 100644 src/testing/pkg/robot2/dao/dao.go create mode 100644 src/testing/pkg/robot2/manager.go diff --git a/make/migrations/postgresql/0050_2.2.0_schema.up.sql b/make/migrations/postgresql/0050_2.2.0_schema.up.sql index 216e74329..02bd12d38 100644 --- a/make/migrations/postgresql/0050_2.2.0_schema.up.sql +++ b/make/migrations/postgresql/0050_2.2.0_schema.up.sql @@ -1,4 +1,5 @@ ALTER TABLE schedule ADD COLUMN IF NOT EXISTS cron_type varchar(64); +ALTER TABLE robot ADD COLUMN IF NOT EXISTS secret varchar(2048); DO $$ DECLARE diff --git a/src/pkg/robot2/dao/dao.go b/src/pkg/robot2/dao/dao.go new file mode 100644 index 000000000..7beab0458 --- /dev/null +++ b/src/pkg/robot2/dao/dao.go @@ -0,0 +1,139 @@ +package dao + +import ( + "context" + "github.com/goharbor/harbor/src/lib/errors" + "github.com/goharbor/harbor/src/lib/orm" + "github.com/goharbor/harbor/src/lib/q" + "github.com/goharbor/harbor/src/pkg/robot2/model" + "time" +) + +// DAO defines the interface to access the robot data model +type DAO interface { + // Create ... + Create(ctx context.Context, r *model.Robot) (int64, error) + + // Update ... + Update(ctx context.Context, r *model.Robot, props ...string) error + + // Get ... + Get(ctx context.Context, id int64) (*model.Robot, error) + + // Count returns the total count of robots according to the query + Count(ctx context.Context, query *q.Query) (total int64, err error) + + // List ... + List(ctx context.Context, query *q.Query) ([]*model.Robot, error) + + // Delete ... + Delete(ctx context.Context, id int64) error + + // DeleteByProjectID ... + DeleteByProjectID(ctx context.Context, projectID int64) error +} + +// New creates a default implementation for Dao +func New() DAO { + return &dao{} +} + +type dao struct{} + +func (d *dao) Create(ctx context.Context, r *model.Robot) (int64, error) { + ormer, err := orm.FromContext(ctx) + if err != nil { + return 0, err + } + r.CreationTime = time.Now() + id, err := ormer.Insert(r) + if err != nil { + return 0, orm.WrapConflictError(err, "robot account %d:%s already exists", r.ProjectID, r.Name) + } + return id, err +} + +func (d *dao) Update(ctx context.Context, r *model.Robot, props ...string) error { + ormer, err := orm.FromContext(ctx) + if err != nil { + return err + } + n, err := ormer.Update(r, props...) + if err != nil { + return err + } + if n == 0 { + return errors.NotFoundError(nil).WithMessage("robot %d not found", r.ID) + } + return nil +} + +func (d *dao) Get(ctx context.Context, id int64) (*model.Robot, error) { + r := &model.Robot{ + ID: id, + } + ormer, err := orm.FromContext(ctx) + if err != nil { + return nil, err + } + if err := ormer.Read(r); err != nil { + return nil, orm.WrapNotFoundError(err, "robot %d not found", id) + } + return r, nil +} + +func (d *dao) Count(ctx context.Context, query *q.Query) (int64, error) { + query = q.MustClone(query) + query.Sorting = "" + query.PageNumber = 0 + query.PageSize = 0 + + qs, err := orm.QuerySetter(ctx, &model.Robot{}, query) + if err != nil { + return 0, err + } + return qs.Count() +} + +func (d *dao) Delete(ctx context.Context, id int64) error { + ormer, err := orm.FromContext(ctx) + if err != nil { + return err + } + n, err := ormer.Delete(&model.Robot{ + ID: id, + }) + if err != nil { + return err + } + if n == 0 { + return errors.NotFoundError(nil).WithMessage("robot account %d not found", id) + } + return nil +} + +func (d *dao) List(ctx context.Context, query *q.Query) ([]*model.Robot, error) { + robots := []*model.Robot{} + + qs, err := orm.QuerySetter(ctx, &model.Robot{}, query) + if err != nil { + return nil, err + } + if _, err = qs.All(&robots); err != nil { + return nil, err + } + return robots, nil +} + +func (d *dao) DeleteByProjectID(ctx context.Context, projectID int64) error { + qs, err := orm.QuerySetter(ctx, &model.Robot{}, &q.Query{ + Keywords: map[string]interface{}{ + "project_id": projectID, + }, + }) + if err != nil { + return err + } + _, err = qs.Delete() + return err +} diff --git a/src/pkg/robot2/dao/dao_test.go b/src/pkg/robot2/dao/dao_test.go new file mode 100644 index 000000000..4abdd0097 --- /dev/null +++ b/src/pkg/robot2/dao/dao_test.go @@ -0,0 +1,156 @@ +package dao + +import ( + "github.com/goharbor/harbor/src/lib/errors" + "github.com/goharbor/harbor/src/lib/orm" + "github.com/goharbor/harbor/src/lib/q" + "github.com/goharbor/harbor/src/pkg/robot2/model" + htesting "github.com/goharbor/harbor/src/testing" + "github.com/stretchr/testify/suite" + "testing" +) + +type DaoTestSuite struct { + htesting.Suite + dao DAO + + robotID1 int64 + robotID2 int64 + robotID3 int64 + robotID4 int64 +} + +func (suite *DaoTestSuite) SetupSuite() { + suite.Suite.SetupSuite() + suite.dao = New() + suite.Suite.ClearTables = []string{"robot"} + suite.robots() +} + +func (suite *DaoTestSuite) robots() { + var err error + suite.robotID1, err = suite.dao.Create(orm.Context(), &model.Robot{ + Name: "test1", + Description: "test1 description", + ProjectID: 1, + Secret: suite.RandString(10), + }) + suite.Nil(err) + + suite.robotID2, err = suite.dao.Create(orm.Context(), &model.Robot{ + Name: "test2", + Description: "test2 description", + ProjectID: 1, + Secret: suite.RandString(10), + }) + suite.Nil(err) + + suite.robotID3, err = suite.dao.Create(orm.Context(), &model.Robot{ + Name: "test3", + Description: "test3 description", + ProjectID: 1, + Secret: suite.RandString(10), + }) + suite.Nil(err) + + suite.robotID4, err = suite.dao.Create(orm.Context(), &model.Robot{ + Name: "test4", + Description: "test4 description", + ProjectID: 2, + Secret: suite.RandString(10), + }) + suite.Nil(err) +} + +func (suite *DaoTestSuite) TestCreate() { + r := &model.Robot{ + Name: "test1", + Description: "test1 description", + ProjectID: 1, + Secret: suite.RandString(10), + } + _, err := suite.dao.Create(orm.Context(), r) + suite.NotNil(err) + suite.True(errors.IsErr(err, errors.ConflictCode)) +} + +func (suite *DaoTestSuite) TestDelete() { + err := suite.dao.Delete(orm.Context(), 1234) + suite.Require().NotNil(err) + suite.True(errors.IsErr(err, errors.NotFoundCode)) + + err = suite.dao.Delete(orm.Context(), suite.robotID2) + suite.Nil(err) +} + +func (suite *DaoTestSuite) TestList() { + robots, err := suite.dao.List(orm.Context(), &q.Query{ + Keywords: map[string]interface{}{ + "name": "test3", + }, + }) + suite.Require().Nil(err) + suite.Equal(suite.robotID3, robots[0].ID) +} + +func (suite *DaoTestSuite) TestGet() { + _, err := suite.dao.Get(orm.Context(), 1234) + suite.Require().NotNil(err) + suite.True(errors.IsErr(err, errors.NotFoundCode)) + + r, err := suite.dao.Get(orm.Context(), suite.robotID3) + suite.Nil(err) + suite.Equal("test3", r.Name) +} + +func (suite *DaoTestSuite) TestCount() { + // nil query + total, err := suite.dao.Count(orm.Context(), nil) + suite.Nil(err) + suite.True(total > 0) + + // query by name + total, err = suite.dao.Count(orm.Context(), &q.Query{ + Keywords: map[string]interface{}{ + "name": "test3", + }, + }) + suite.Nil(err) + suite.Equal(int64(1), total) +} + +func (suite *DaoTestSuite) TestUpdate() { + r := &model.Robot{ + ID: suite.robotID3, + Description: "after test3 update", + } + + err := suite.dao.Update(orm.Context(), r) + suite.Nil(err) + + r1, err := suite.dao.Get(orm.Context(), r.ID) + suite.Equal("after test3 update", r1.Description) +} + +func (suite *DaoTestSuite) TestDeleteByProjectID() { + robots, err := suite.dao.List(orm.Context(), &q.Query{ + Keywords: map[string]interface{}{ + "project_id": 2, + }, + }) + suite.Equal(1, len(robots)) + + err = suite.dao.DeleteByProjectID(orm.Context(), 2) + suite.Nil(err) + + robots, err = suite.dao.List(orm.Context(), &q.Query{ + Keywords: map[string]interface{}{ + "project_id": 2, + }, + }) + suite.Equal(0, len(robots)) +} + +func TestDaoTestSuite(t *testing.T) { + suite.Run(t, &DaoTestSuite{}) +} diff --git a/src/pkg/robot2/manager.go b/src/pkg/robot2/manager.go new file mode 100644 index 000000000..1ceb342a7 --- /dev/null +++ b/src/pkg/robot2/manager.go @@ -0,0 +1,85 @@ +package robot2 + +import ( + "context" + "github.com/goharbor/harbor/src/lib/q" + "github.com/goharbor/harbor/src/pkg/robot2/dao" + "github.com/goharbor/harbor/src/pkg/robot2/model" +) + +var ( + // Mgr is a global variable for the default robot account manager implementation + Mgr = NewManager() +) + +// Manager ... +type Manager interface { + // Get ... + Get(ctx context.Context, id int64) (*model.Robot, error) + + // Count returns the total count of robots according to the query + Count(ctx context.Context, query *q.Query) (total int64, err error) + + // Create ... + Create(ctx context.Context, m *model.Robot) (int64, error) + + // Delete ... + Delete(ctx context.Context, id int64) error + + // DeleteByProjectID ... + DeleteByProjectID(ctx context.Context, projectID int64) error + + // Update ... + Update(ctx context.Context, m *model.Robot) error + + // List ... + List(ctx context.Context, query *q.Query) ([]*model.Robot, error) +} + +var _ Manager = &manager{} + +type manager struct { + dao dao.DAO +} + +// NewManager return a new instance of defaultRobotManager +func NewManager() Manager { + return &manager{ + dao: dao.New(), + } +} + +// Get ... +func (m *manager) Get(ctx context.Context, id int64) (*model.Robot, error) { + return m.dao.Get(ctx, id) +} + +// Count ... +func (m *manager) Count(ctx context.Context, query *q.Query) (total int64, err error) { + return m.dao.Count(ctx, query) +} + +// Create ... +func (m *manager) Create(ctx context.Context, r *model.Robot) (int64, error) { + return m.dao.Create(ctx, r) +} + +// Delete ... +func (m *manager) Delete(ctx context.Context, id int64) error { + return m.dao.Delete(ctx, id) +} + +// DeleteByProjectID ... +func (m *manager) DeleteByProjectID(ctx context.Context, projectID int64) error { + return m.dao.DeleteByProjectID(ctx, projectID) +} + +// Update ... +func (m *manager) Update(ctx context.Context, r *model.Robot) error { + return m.dao.Update(ctx, r) +} + +// List ... +func (m *manager) List(ctx context.Context, query *q.Query) ([]*model.Robot, error) { + return m.dao.List(ctx, query) +} diff --git a/src/pkg/robot2/manager_test.go b/src/pkg/robot2/manager_test.go new file mode 100644 index 000000000..ac7ce2758 --- /dev/null +++ b/src/pkg/robot2/manager_test.go @@ -0,0 +1,76 @@ +package robot2 + +import ( + "context" + "github.com/goharbor/harbor/src/pkg/robot2/model" + "github.com/goharbor/harbor/src/testing/mock" + "github.com/goharbor/harbor/src/testing/pkg/robot2/dao" + "github.com/stretchr/testify/suite" + "testing" +) + +type managerTestSuite struct { + suite.Suite + mgr *manager + dao *dao.DAO +} + +func (m *managerTestSuite) SetupTest() { + m.dao = &dao.DAO{} + m.mgr = &manager{ + dao: m.dao, + } +} + +func (m *managerTestSuite) TestCreate() { + m.dao.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil) + _, err := m.mgr.Create(context.Background(), &model.Robot{}) + m.Nil(err) + m.dao.AssertExpectations(m.T()) +} + +func (m *managerTestSuite) TestCount() { + m.dao.On("Count", mock.Anything, mock.Anything).Return(int64(1), nil) + n, err := m.mgr.Count(context.Background(), nil) + m.Nil(err) + m.Equal(int64(1), n) + m.dao.AssertExpectations(m.T()) +} + +func (m *managerTestSuite) TestDelete() { + m.dao.On("Delete", mock.Anything, mock.Anything).Return(nil) + err := m.mgr.Delete(context.Background(), 1) + m.Nil(err) + m.dao.AssertExpectations(m.T()) +} + +func (m *managerTestSuite) TestDeleteByProjectID() { + m.dao.On("DeleteByProjectID", mock.Anything, mock.Anything).Return(nil) + err := m.mgr.DeleteByProjectID(context.Background(), 1) + m.Nil(err) + m.dao.AssertExpectations(m.T()) +} + +func (m *managerTestSuite) TestUpdate() { + m.dao.On("Update", mock.Anything, mock.Anything).Return(nil) + err := m.mgr.Update(context.Background(), &model.Robot{}) + m.Nil(err) + m.dao.AssertExpectations(m.T()) +} + +func (m *managerTestSuite) TestList() { + m.dao.On("List", mock.Anything, mock.Anything).Return([]*model.Robot{ + { + ID: 1, + Name: "robot", + }, + }, nil) + rpers, err := m.mgr.List(context.Background(), nil) + m.Nil(err) + m.Equal(1, len(rpers)) + m.dao.AssertExpectations(m.T()) +} + +func TestManager(t *testing.T) { + suite.Run(t, &managerTestSuite{}) +} diff --git a/src/pkg/robot2/model/model.go b/src/pkg/robot2/model/model.go new file mode 100644 index 000000000..2c74fba17 --- /dev/null +++ b/src/pkg/robot2/model/model.go @@ -0,0 +1,30 @@ +package model + +import ( + "time" + + "github.com/astaxie/beego/orm" +) + +func init() { + orm.RegisterModel(&Robot{}) +} + +// Robot holds the details of a robot. +type Robot struct { + ID int64 `orm:"pk;auto;column(id)" json:"id"` + Name string `orm:"column(name)" json:"name"` + Description string `orm:"column(description)" json:"description"` + Secret string `orm:"column(secret)" json:"secret"` + ProjectID int64 `orm:"column(project_id)" json:"project_id"` + ExpiresAt int64 `orm:"column(expiresat)" json:"expires_at"` + Disabled bool `orm:"column(disabled)" json:"disabled"` + Visible bool `orm:"column(visible)" json:"-"` + CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"` + UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"` +} + +// TableName ... +func (r *Robot) TableName() string { + return "robot" +} diff --git a/src/testing/pkg/pkg.go b/src/testing/pkg/pkg.go index c6d78c590..10a7cf0b5 100644 --- a/src/testing/pkg/pkg.go +++ b/src/testing/pkg/pkg.go @@ -31,3 +31,5 @@ package pkg //go:generate mockery --case snake --dir ../../pkg/robot/dao --name RobotAccountDao --output ./robot/dao --outpkg dao //go:generate mockery --case snake --dir ../../pkg/rbac --name Manager --output ./rbac --outpkg rbac //go:generate mockery --case snake --dir ../../pkg/rbac/dao --name DAO --output ./rbac/dao --outpkg dao +//go:generate mockery --case snake --dir ../../pkg/robot2 --name Manager --output ./robot2 --outpkg robot2 +//go:generate mockery --case snake --dir ../../pkg/robot2/dao --name DAO --output ./robot2/dao --outpkg dao diff --git a/src/testing/pkg/robot2/dao/dao.go b/src/testing/pkg/robot2/dao/dao.go new file mode 100644 index 000000000..b3b46d3ab --- /dev/null +++ b/src/testing/pkg/robot2/dao/dao.go @@ -0,0 +1,155 @@ +// Code generated by mockery v2.1.0. DO NOT EDIT. + +package dao + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + + model "github.com/goharbor/harbor/src/pkg/robot2/model" + + q "github.com/goharbor/harbor/src/lib/q" +) + +// DAO is an autogenerated mock type for the DAO type +type DAO struct { + mock.Mock +} + +// Count provides a mock function with given fields: ctx, query +func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { + ret := _m.Called(ctx, query) + + var r0 int64 + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok { + r0 = rf(ctx, query) + } else { + r0 = ret.Get(0).(int64) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { + r1 = rf(ctx, query) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Create provides a mock function with given fields: ctx, r +func (_m *DAO) Create(ctx context.Context, r *model.Robot) (int64, error) { + ret := _m.Called(ctx, r) + + var r0 int64 + if rf, ok := ret.Get(0).(func(context.Context, *model.Robot) int64); ok { + r0 = rf(ctx, r) + } else { + r0 = ret.Get(0).(int64) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *model.Robot) error); ok { + r1 = rf(ctx, r) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Delete provides a mock function with given fields: ctx, id +func (_m *DAO) Delete(ctx context.Context, id int64) error { + ret := _m.Called(ctx, id) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteByProjectID provides a mock function with given fields: ctx, projectID +func (_m *DAO) DeleteByProjectID(ctx context.Context, projectID int64) error { + ret := _m.Called(ctx, projectID) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { + r0 = rf(ctx, projectID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Get provides a mock function with given fields: ctx, id +func (_m *DAO) Get(ctx context.Context, id int64) (*model.Robot, error) { + ret := _m.Called(ctx, id) + + var r0 *model.Robot + if rf, ok := ret.Get(0).(func(context.Context, int64) *model.Robot); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Robot) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// List provides a mock function with given fields: ctx, query +func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.Robot, error) { + ret := _m.Called(ctx, query) + + var r0 []*model.Robot + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*model.Robot); ok { + r0 = rf(ctx, query) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*model.Robot) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { + r1 = rf(ctx, query) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Update provides a mock function with given fields: ctx, r, props +func (_m *DAO) Update(ctx context.Context, r *model.Robot, props ...string) error { + _va := make([]interface{}, len(props)) + for _i := range props { + _va[_i] = props[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, r) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *model.Robot, ...string) error); ok { + r0 = rf(ctx, r, props...) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/src/testing/pkg/robot2/manager.go b/src/testing/pkg/robot2/manager.go new file mode 100644 index 000000000..b166864da --- /dev/null +++ b/src/testing/pkg/robot2/manager.go @@ -0,0 +1,147 @@ +// Code generated by mockery v2.1.0. DO NOT EDIT. + +package robot2 + +import ( + context "context" + + model "github.com/goharbor/harbor/src/pkg/robot2/model" + mock "github.com/stretchr/testify/mock" + + q "github.com/goharbor/harbor/src/lib/q" +) + +// Manager is an autogenerated mock type for the Manager type +type Manager struct { + mock.Mock +} + +// Count provides a mock function with given fields: ctx, query +func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { + ret := _m.Called(ctx, query) + + var r0 int64 + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok { + r0 = rf(ctx, query) + } else { + r0 = ret.Get(0).(int64) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { + r1 = rf(ctx, query) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Create provides a mock function with given fields: ctx, m +func (_m *Manager) Create(ctx context.Context, m *model.Robot) (int64, error) { + ret := _m.Called(ctx, m) + + var r0 int64 + if rf, ok := ret.Get(0).(func(context.Context, *model.Robot) int64); ok { + r0 = rf(ctx, m) + } else { + r0 = ret.Get(0).(int64) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *model.Robot) error); ok { + r1 = rf(ctx, m) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Delete provides a mock function with given fields: ctx, id +func (_m *Manager) Delete(ctx context.Context, id int64) error { + ret := _m.Called(ctx, id) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteByProjectID provides a mock function with given fields: ctx, projectID +func (_m *Manager) DeleteByProjectID(ctx context.Context, projectID int64) error { + ret := _m.Called(ctx, projectID) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { + r0 = rf(ctx, projectID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Get provides a mock function with given fields: ctx, id +func (_m *Manager) Get(ctx context.Context, id int64) (*model.Robot, error) { + ret := _m.Called(ctx, id) + + var r0 *model.Robot + if rf, ok := ret.Get(0).(func(context.Context, int64) *model.Robot); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Robot) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// List provides a mock function with given fields: ctx, query +func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.Robot, error) { + ret := _m.Called(ctx, query) + + var r0 []*model.Robot + if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*model.Robot); ok { + r0 = rf(ctx, query) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*model.Robot) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok { + r1 = rf(ctx, query) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Update provides a mock function with given fields: ctx, m +func (_m *Manager) Update(ctx context.Context, m *model.Robot) error { + ret := _m.Called(ctx, m) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *model.Robot) error); ok { + r0 = rf(ctx, m) + } else { + r0 = ret.Error(0) + } + + return r0 +}