侧边栏壁纸
博主头像
问道

问道的小花园,总能给你带来惊喜

  • 累计撰写 68 篇文章
  • 累计创建 35 个标签
  • 累计收到 3 条评论

混合云资产管理项目(三)

问道
2023-01-26 / 0 评论 / 0 点赞 / 295 阅读 / 9,208 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-01-26,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

resource模块的测试,对在前面编写的逻辑方法进行单元测试

测试前的配置加载和依赖准备

test/tools/setup.go—测试用例的配置初始化加载

func DevelopmentSetup() {
  // 初始化日志实例
  zap.DevelopmentSetup()

  // 初始化配置, 提前配置好/etc/unit_test.env
  err := conf.LoadConfigFromEnv()
  if err != nil {
    panic(err)
  }

  // 初始化全局app
  if err := app.InitAllApp(); err != nil {
    panic(err)
  }
}

etc/unit_test.env—在文件中配置测试的数据库连接

MYSQL_HOST = 
MYSQL_PORT = 3306
MYSQL_USERNAME = root
MYSQL_PASSWORD = 
MYSQL_DATABASE = cmdb

apps/resource/impl/resource_dao.go

构造对用的tablename

type ResourceId struct {
  ResourceId string `json:"resource_id"`
}

type Spec struct {
  *ResourceId
  *resource.Spec
}

func (s *Spec) TableName() string {
  return "resource_spec"
}

type Cost struct {
  *ResourceId
  *resource.Cost
}

func (m *Cost) TableName() string {
  return "resource_cost"
}

type Status struct {
  *ResourceId
  *resource.Status
}

func (m *Status) TableName() string {
  return "resource_status"
}

type Tag struct {
  *ResourceId
  *resource.Tag
}

func (m *Tag) TableName() string {
  return "resource_tag"
}

编写具体测试函数

apps/resource/impl/impl_test.go

func init() {
  tools.DevelopmentSetup()
  impl = app.GetInternalApp(resource.AppName).(resource.Service)
}

var (
  impl resource.Service
  ctx  = context.Background()
)

func TestSaveResource(t *testing.T) {
  res := &resource.Resource{
    Meta: &resource.Meta{
      ResourceId: "instance_01",
      Domain:     "default",
      Namespace:  "default",
      Env:        "测试环境",
      SyncAt:     time.Now().Unix(),
    },
    Spec: &resource.Spec{
      Vendor:       resource.VENDOR_ALIYUN,
      ResourceType: resource.TYPE_HOST,
      Name:         "test01",
    },
    Cost: &resource.Cost{
      RealCost: 80000,
    },
    Status: &resource.Status{
      Phase: "Running",
    },
    Tags: []*resource.Tag{
      {Key: "app", Value: "app_01", Describe: "应用01"},
      {Key: "app", Value: "app_02", Describe: "应用02"},
    },
  }
  _, err := impl.SaveResource(ctx, res)
  if err != nil {
    t.Fatal(err)
  }
}

点击测试,结果如下。会显示SQL的具体执行语句

=== RUN   TestSaveResource

2023/01/26 12:19:27 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:23
[24.516ms] [rows:1] INSERT INTO `resource_meta` (`resource_id`,`domain`,`namespace`,`env`,`sync_at`,`credential_id`,`create_at`,`serial_number`) VALUES ('instance_02','default','default','测试环境',1674706767,'',0,'')

2023/01/26 12:19:27 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:27
[26.260ms] [rows:1] INSERT INTO `resource_spec` (`resource_id`,`vendor`,`resource_type`,`region`,`zone`,`owner`,`name`,`category`,`type`,`description`,`expire_at`,`update_at`,`release_protection`,`cpu`,`gpu`,`memory`,`storage`,`band_width`) VALUES ('instance_02',0,0,'','','','test01','','','',0,0,NULL,0,0,0,0,0)

2023/01/26 12:19:27 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:31
[24.898ms] [rows:1] INSERT INTO `resource_status` (`resource_id`,`phase`,`lock_mode`,`lock_reason`,`public_ip`,`private_ip`) VALUES ('instance_02','Running','','','','')

2023/01/26 12:19:27 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:35
[23.360ms] [rows:1] INSERT INTO `resource_cost` (`resource_id`,`pay_mode`,`pay_mode_detail`,`sale_price`,`real_cost`,`policy`,`unit_price`) VALUES ('instance_02',NULL,'',0.000000,80000.000000,0.000000,0.000000)

2023/01/26 12:19:27 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:40
[24.273ms] [rows:0] DELETE FROM `resource_tag` WHERE resource_id = 'instance_02'

2023/01/26 12:19:27 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:45
[23.755ms] [rows:1] INSERT INTO `resource_tag` (`resource_id`,`purpose`,`key`,`value`,`describe`,`weight`,`read_only`,`hidden`) VALUES ('instance_02',0,'app','app_01','应用01',0,fals  e,false)

2023/01/26 12:19:27 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:45
[15.866ms] [rows:1] INSERT INTO `resource_tag` (`resource_id`,`purpose`,`key`,`value`,`describe`,`weight`,`read_only`,`hidden`) VALUES ('instance_02',0,'app','app_02','应用02',0,fals  e,false)
--- PASS: TestSaveResource (0.19s)
PASS

apps/resource/impl/resource.go

添加查询方法

func (s *service) QueryResource(ctx context.Context, req *resource.QueryResourceRequest) (
  *resource.ResourceSet, error) {
  // 首先查询Meta表
  // LEFT JOIN

  // 都是用orm, JOIN
  // 完全使用SQL
  //   SELECT
  //   *
  // FROM
  //   resource_meta m
  //   LEFT JOIN resource_spec s ON s.resource_id = m.resource_id
  //   LEFT JOIN resource_cost c ON c.resource_id = m.resource_id
  //   LEFT JOIN resource_status t ON t.resource_id = m.resource_id
  // WHERE m.resource_id='instance_01';
  // orm作为SQL builder 来构建复杂SQL
  query := s.db.WithContext(ctx).Table("resource_meta m").
    Joins("LEFT JOIN resource_spec s ON s.resource_id = m.resource_id").
    Joins("LEFT JOIN resource_cost c ON c.resource_id = m.resource_id").
    Joins("LEFT JOIN resource_status t ON t.resource_id = m.resource_id")
  if req.Domain != "" {
    query = query.Where("m.domain = ?", req.Domain)
  }
  if req.Namespace != "" {
    query = query.Where("m.namespace = ?", req.Namespace)
  }
  if req.Env != "" {
    query = query.Where("m.env = ?", req.Env)
  }
  if req.Status != "" {
    query = query.Where("m.env = ?", req.Env)
  }
  if req.Type != nil {
    query = query.Where("s.resource_type = ?", req.Env)
  }
  if len(req.ResourceIds) > 0 {
    query = query.Where("m.resource_id IN (?)", req.ResourceIds)
  }

  temp := NewResourceSet()
  err := query.
    Order("m.create_at DESC").
    Offset(int(req.Page.ComputeOffset())).
    Limit(int(req.Page.PageSize)).
    Scan(&temp.Items).
    Error

  if err != nil {
    return nil, err
  }

  // 数据库里面的数据 和 接口的数据不一定匹配
  // 需要在Dao进行转换
  set := temp.ResourceSet()

  // 统计有多少条
  err = query.Count(&set.Total).Error
  if err != nil {
    return nil, err
  }
  return set, nil
}

apps/resource/impl/impl_test.go

补充查询resource的测试函数

func TestQueryResource(t *testing.T) {
  req := resource.NewQueryResourceRequest()
  req.ResourceIds = []string{"instance_04"}
  resp, err := impl.QueryResource(ctx, req)
  if err != nil {
    t.Fatal(err)
  }
  t.Log(resp)
}

测试结果

=== RUN   TestQueryResource

2023/01/26 12:32:49 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource.go:68
[19.209ms] [rows:1] SELECT * FROM resource_meta m LEFT JOIN resource_spec s ON s.resource_id = m.resource_id LEFT JOIN resource_cost c ON c.resource_id = m.resource_id LEFT JOIN resource_status t ON t.resource_id = m.resource_id WHERE m.resource_id IN ('instance_02') ORDER BY m.create_at DESC LIMIT 20

2023/01/26 12:32:49 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource.go:80
[19.176ms] [rows:1] SELECT count(*) FROM resource_meta m LEFT JOIN resource_spec s ON s.resource_id = m.resource_id LEFT JOIN resource_cost c ON c.resource_id = m.resource_id LEFT JOIN resource_status t ON t.resource_id = m.resource_id WHERE m.resource_id IN ('instance_02') LIMIT 20
    impl_test.go:78: total:1  items:{meta:{resource_id:"instance_02"  domain:"default"  namespace:"default"  env:"测试环境"  sync_at:    1674706767}  spec:{name:"test01"}  cost:{real_cost:80000}  status:{phase:"Running"}}
--- PASS: TestQueryResource (0.04s)
PASS

apps/resource/impl/resource.go

添加删除资源的方法

func (s *service) DeleteResource(ctx context.Context, res *resource.DeleteResourceRequest) (
  *resource.DeleteResourceRequest, error) {
  ins := resource.NewResource()
  err := s.delete(ctx, res)
  if err != nil {
    panic(err)
  }
  ins.Meta.ResourceId = res.ResourceId
  return nil, nil
}

apps/resource/impl/resource_dao.go

补充删除对数据库的交互逻辑,使用事务

// 多表联合删除,开启事务
func (s *service) delete(ctx context.Context, res *resource.DeleteResourceRequest) (err error) {
  // 开启事务
  tx := s.db.WithContext(ctx).Begin()
  defer func() {
    if err != nil {
      tx.Rollback()
      return
    }
    err := tx.Commit().Error
    if err != nil {
      s.log.Errorf("commit error, %s", err)
    }
  }()

  //DELETE FROM `resource_meta` WHERE resource_id = 'instance_01'
  err = tx.Table("resource_meta").Where("resource_id = ?", res.ResourceId).Delete(&resource.Meta{}).Error
  if err != nil {
    return
  }
  err = tx.Table("resource_spec").Where("resource_id = ?", res.ResourceId).Delete(&resource.Spec{}).Error
  if err != nil {
    return
  }
  err = tx.Table("resource_cost").Where("resource_id = ?", res.ResourceId).Delete(&resource.Status{}).Error
  if err != nil {
    return
  }
  err = tx.Table("resource_status").Where("resource_id = ?", res.ResourceId).Delete(&resource.Cost{}).Error
  if err != nil {
    return
  }

  err = tx.Table("resource_tag").Where("resource_id = ?", res.ResourceId).Delete(&Tag{}).Error
  if err != nil {
    return
  }

  return
}

apps/resource/impl/impl_test.go

删除resource的测试方法

func TestDeleteResource(t *testing.T) {
  req := new(resource.DeleteResourceRequest)
  req.ResourceId = "instance_03"
  req.Namespace = "default"
  resp, err := impl.DeleteResource(ctx, req)
  if err != nil {
    t.Fatal(err)
  }
  t.Log(resp)
}

测试结果

=== RUN   TestDeleteResource

2023/01/26 12:38:20 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:69
[23.023ms] [rows:1] DELETE FROM `resource_meta` WHERE resource_id = 'instance_03'

2023/01/26 12:38:20 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:73
[22.810ms] [rows:1] DELETE FROM `resource_spec` WHERE resource_id = 'instance_03'

2023/01/26 12:38:20 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:77
[23.311ms] [rows:1] DELETE FROM `resource_cost` WHERE resource_id = 'instance_03'

2023/01/26 12:38:20 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:81
[21.121ms] [rows:1] DELETE FROM `resource_status` WHERE resource_id = 'instance_03'

2023/01/26 12:38:20 F:/go/project/0-shizhanxiangmu/myCMDB/mycmdb/apps/resource/impl/resource_dao.go:86
[22.898ms] [rows:2] DELETE FROM `resource_tag` WHERE resource_id = 'instance_03'

总结

至此,完成了对resource模块数据结构的建立,以及添加,查询和删除资源的逻辑,以及对它们的单元测试。

0

评论区