feat(api): 增强 API 测试框架功能与文档

- 在 Api 结构中新增 lastResp 字段以存储最近请求的响应
- 添加 Verify 方法,支持自定义校验函数并返回 ApiCase 构建器
- 新增 Resp 方法,获取最近一次请求的响应以便于断言
- 在 TestCollector 中添加 Visited 字段,记录已调用的路径
- 更新 GenerateSwagger 方法,支持部分运行时保留未运行端点的已有数据
- 完善文档,增加用例编写范式和示例,提升测试框架的可用性与易用性
This commit is contained in:
2026-03-30 01:55:07 +08:00
parent 3681564ca4
commit 37a67f5810
14 changed files with 2665 additions and 639 deletions
+81 -3
View File
@@ -1,11 +1,89 @@
package app
import (
"fmt"
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
var CacheTest = CtrTest{
"all": {Desc: "缓存全部测试", Func: func(a *Api) { a.Get("缓存全部测试", 0) }},
"compat": {Desc: "缓存兼容模式测试", Func: func(a *Api) { a.Get("兼容模式测试", 0) }},
"batch": {Desc: "批量缓存测试", Func: func(a *Api) { a.Get("批量缓存测试", 0) }},
"all": {Desc: "缓存全部测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if !result.GetBool("success") {
return fmt.Errorf("缓存测试未全部通过")
}
if result.GetString("cache_mode") == "" {
return fmt.Errorf("cache_mode 字段缺失")
}
tests := result.GetSlice("tests")
if len(tests) == 0 {
return fmt.Errorf("tests 数组为空")
}
for i := 0; i < len(tests); i++ {
item := tests.GetMap(i)
if item != nil && !item.GetBool("result") {
return fmt.Errorf("子项 '%s' 失败", item.GetString("name"))
}
}
return nil
}).Get("缓存全部测试", 0, Map{
"name": "sample",
"cache_mode": "sample",
"success": true,
"cleanup": "sample",
"tests": Slice{Map{
"name": "sample", "result": true,
}},
})
}},
// 兼容模式下的老表回退/写新删老操作在测试事务隔离下已知异常(cache API
// 使用独立连接,不在 testTx 事务内),因此只校验结构完整性
"compat": {Desc: "缓存兼容模式测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if result.GetString("test_name") == "" {
return fmt.Errorf("test_name 字段缺失")
}
if result.GetString("timestamp") == "" {
return fmt.Errorf("timestamp 字段缺失")
}
tests := result.GetSlice("tests")
if len(tests) == 0 {
return fmt.Errorf("tests 数组为空")
}
return nil
}).Get("兼容模式测试", 0, Map{
"test_name": "sample",
"timestamp": "sample",
"success": true,
"tests": Slice{Map{
"name": "sample", "result": true,
}},
})
}},
"batch": {Desc: "批量缓存测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
msg := result.GetString("message")
if msg == "" {
return fmt.Errorf("message 字段为空")
}
return nil
}).Get("批量缓存测试", 0, Map{
"message": "sample",
})
}},
}
+81 -3
View File
@@ -1,12 +1,90 @@
package app
import (
"fmt"
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
var DmdbTest = CtrTest{
"tables": {Desc: "查询达梦所有表", Func: func(a *Api) { a.Get("USER_TABLES 查询", 0) }},
"describe": {Desc: "查询达梦表结构", Func: func(a *Api) { a.Query(Map{"table": "admin"}).Get("USER_TAB_COLUMNS 查询", 0) }},
"rawsql": {Desc: "达梦原生 SQL 测试", Func: func(a *Api) { a.Get("达梦原生SQL", 0) }},
"tables": {Desc: "查询达梦所有表", Func: func(a *Api) {
if a.DB().Type != "dm" && a.DB().Type != "dameng" {
return
}
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
tables := result.GetSlice("tables")
if tables == nil {
return fmt.Errorf("tables 字段缺失")
}
if len(tables) == 0 {
return fmt.Errorf("达梦表列表为空,至少应有测试表")
}
return nil
}).Get("USER_TABLES 查询", 0, Map{
"tables": Slice{Map{"name": "sample"}},
})
}},
"describe": {Desc: "查询达梦表结构", Func: func(a *Api) {
if a.DB().Type != "dm" && a.DB().Type != "dameng" {
return
}
// ======== 错误用例 ========
a.Get("不传table参数", 1, "请提供 table 参数")
// ======== 正确请求 + 结构校验 + Verify ========
a.Query(Map{"table": "admin"}).
Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if result.GetString("table") != "admin" {
return fmt.Errorf("table 期望 'admin', 实际 '%s'", result.GetString("table"))
}
columns := result.GetSlice("columns")
if len(columns) == 0 {
return fmt.Errorf("columns 为空,admin 表应有字段定义")
}
return nil
}).
Get("USER_TAB_COLUMNS 查询", 0, Map{
"table": "sample", "columns": Slice{Map{"name": "sample", "type": "sample"}}, "sample_data": Slice{},
})
}},
"rawsql": {Desc: "达梦原生 SQL 测试", Func: func(a *Api) {
if a.DB().Type != "dm" && a.DB().Type != "dameng" {
return
}
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if !result.GetBool("success") {
return fmt.Errorf("达梦原生 SQL 测试未通过")
}
tests := result.GetSlice("tests")
if len(tests) == 0 {
return fmt.Errorf("tests 数组为空")
}
for i := 0; i < len(tests); i++ {
item := tests.GetMap(i)
if item != nil && !item.GetBool("result") {
return fmt.Errorf("子项 '%s' 失败", item.GetString("name"))
}
}
return nil
}).Get("达梦原生SQL", 0, Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample", "result": true}},
})
}},
}
+201 -27
View File
@@ -8,25 +8,109 @@ import (
)
var ExpectDemoTest = CtrTest{
"string_result": {Desc: "返回字符串", Func: func(a *Api) {
a.Get("result是字符串", 0, "操作成功")
// ─── 基础类型结构校验 + Verify 值断言 ──────────────────
"string_result": {Desc: "返回字符串-结构+值校验", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetString("result")
if result != "操作成功" {
return fmt.Errorf("result 期望 '操作成功', 实际 '%s'", result)
}
return nil
}).Get("result是字符串", 0, "样本")
}},
"number_result": {Desc: "返回整数", Func: func(a *Api) {
a.Get("result是整数", 0, int64(1))
"number_result": {Desc: "返回整数-结构+值校验", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetCeilInt64("result")
if result != 42 {
return fmt.Errorf("result 期望 42, 实际 %d", result)
}
return nil
}).Get("result是整数", 0, int64(1))
}},
"float_result": {Desc: "返回小数", Func: func(a *Api) {
a.Get("result是小数", 0, float64(1.0))
"float_result": {Desc: "返回小数-结构+值校验", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetFloat64("result")
if result != 3.14 {
return fmt.Errorf("result 期望 3.14, 实际 %f", result)
}
return nil
}).Get("result是小数", 0, float64(1.0))
}},
"bool_result": {Desc: "返回布尔", Func: func(a *Api) {
a.Get("result是布尔", 0, true)
"bool_result": {Desc: "返回布尔-结构+值校验", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetBool("result")
if !result {
return fmt.Errorf("result 期望 true, 实际 false")
}
return nil
}).Get("result是布尔", 0, true)
}},
"map_result": {Desc: "返回对象", Func: func(a *Api) {
a.Get("result是Map-校验字段名和类型", 0, Map{
"map_result": {Desc: "返回对象-结构+值校验", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if result.GetCeilInt64("id") != 1 {
return fmt.Errorf("id 期望 1, 实际 %d", result.GetCeilInt64("id"))
}
if result.GetString("name") != "示例商品" {
return fmt.Errorf("name 期望 '示例商品', 实际 '%s'", result.GetString("name"))
}
if !result.GetBool("in_stock") {
return fmt.Errorf("in_stock 期望 true")
}
return nil
}).Get("result是Map-校验字段名+类型+值", 0, Map{
"id": int64(1), "name": "sample", "price": float64(1.0), "in_stock": true,
})
}},
"nested_result": {Desc: "返回嵌套对象", Func: func(a *Api) {
a.Get("深层嵌套Map+Slice+Map", 0, Map{
"nested_result": {Desc: "返回嵌套对象-结构+值校验", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if result.GetCeilInt64("id") != 100 {
return fmt.Errorf("id 期望 100, 实际 %d", result.GetCeilInt64("id"))
}
if result.GetString("title") != "测试订单" {
return fmt.Errorf("title 期望 '测试订单', 实际 '%s'", result.GetString("title"))
}
customer := result.GetMap("customer")
if customer == nil {
return fmt.Errorf("customer 为 nil")
}
if customer.GetString("name") != "张三" {
return fmt.Errorf("customer.name 期望 '张三', 实际 '%s'", customer.GetString("name"))
}
if customer.GetString("phone") != "13800138000" {
return fmt.Errorf("customer.phone 期望 '13800138000', 实际 '%s'", customer.GetString("phone"))
}
items := result.GetSlice("items")
if len(items) != 2 {
return fmt.Errorf("items 期望 2 条, 实际 %d 条", len(items))
}
firstItem := items.GetMap(0)
if firstItem.GetString("goods_name") != "苹果" {
return fmt.Errorf("items[0].goods_name 期望 '苹果', 实际 '%s'", firstItem.GetString("goods_name"))
}
delivery := result.GetMap("extra").GetMap("delivery")
if delivery == nil {
return fmt.Errorf("extra.delivery 为 nil")
}
if delivery.GetString("address") != "XX路1号" {
return fmt.Errorf("extra.delivery.address 期望 'XX路1号', 实际 '%s'", delivery.GetString("address"))
}
return nil
}).Get("深层嵌套Map+Slice+Map", 0, Map{
"id": int64(1), "title": "sample",
"customer": Map{"id": int64(1), "name": "sample", "phone": "sample"},
"items": Slice{Map{
@@ -37,42 +121,132 @@ var ExpectDemoTest = CtrTest{
},
})
}},
"slice_result": {Desc: "返回数组", Func: func(a *Api) {
a.Get("result直接是Slice", 0, Slice{Map{"id": int64(1), "name": "sample"}})
"slice_result": {Desc: "返回数组-结构+值校验", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetSlice("result")
if len(result) != 2 {
return fmt.Errorf("result 期望 2 条, 实际 %d 条", len(result))
}
first := result.GetMap(0)
if first.GetString("name") != "标签A" {
return fmt.Errorf("result[0].name 期望 '标签A', 实际 '%s'", first.GetString("name"))
}
second := result.GetMap(1)
if second.GetString("name") != "标签B" {
return fmt.Errorf("result[1].name 期望 '标签B', 实际 '%s'", second.GetString("name"))
}
return nil
}).Get("result直接是Slice", 0, Slice{Map{"id": int64(1), "name": "sample"}})
}},
"error_demo": {Desc: "错误+成功混合", Func: func(a *Api) {
a.Form(Map{"name": ""}).Post("名称为空-错误断言", 3, "名称不能为空")
a.Form(Map{"name": "测试"}).Post("名称正确-结构校验", 0, Map{
"id": int64(1), "name": "sample", "created": true,
})
// ─── 错误先行 + 正确请求完整范式 ─────────────────────
"error_demo": {Desc: "错误先行+正确请求完整校验", Func: func(a *Api) {
// ======== 第一步:错误用例(先行) ========
a.Form(Map{"name": ""}).Post("名称为空-缺少必填字段", 3, "名称不能为空")
// ======== 第二步:正确请求 + 结构校验 + Verify 值断言 ========
a.Form(Map{"name": "测试商品"}).
Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if result.GetCeilInt64("id") != 1 {
return fmt.Errorf("id 期望 1, 实际 %d", result.GetCeilInt64("id"))
}
if result.GetString("name") != "测试商品" {
return fmt.Errorf("name 期望 '测试商品', 实际 '%s'", result.GetString("name"))
}
if !result.GetBool("created") {
return fmt.Errorf("created 期望 true")
}
return nil
}).
Post("名称正确-结构+值校验", 0, Map{
"id": int64(1), "name": "sample", "created": true,
})
}},
"no_expect": {Desc: "不传预期(仅校验status)", Func: func(a *Api) {
a.Get("只校验status=0", 0)
"no_expect": {Desc: "最小正确用例-仍需值断言", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if result.GetCeilInt64("id") != 1 {
return fmt.Errorf("id 期望 1, 实际 %d", result.GetCeilInt64("id"))
}
if result.GetString("data") != "无预期校验" {
return fmt.Errorf("data 期望 '无预期校验', 实际 '%s'", result.GetString("data"))
}
return nil
}).Get("有Verify值断言", 0, Map{"id": int64(1), "data": "sample"})
}},
"verify_demo": {Desc: "Verify数据库校验", Func: func(a *Api) {
// ─── Verify 完整范式:错误 → 数据准备 → 正确请求 + 响应断言 + DB 校验 ─
"verify_demo": {Desc: "Verify完整范式演示", Func: func(a *Api) {
// ======== 第一步:错误用例 ========
a.Form(Map{"name": ""}).Post("名称为空", 3, "名称不能为空")
a.Form(Map{"name": "verify_test"}).
// ======== 第二步:准备测试数据 ========
// 先插入一条已有记录,用于后续 DB 校验时确认"新增"行为
a.DB().Insert("test_batch", Map{
"name": "existing_record", "title": "verify_demo", "state": 1,
})
// ======== 第三步:正确请求 + Verify 统一校验 ========
name := "verify_test"
a.Form(Map{"name": name}).
Verify(func(a *Api) error {
row := a.DB().Get("test_batch", "*", Map{"AND": Map{"name": "verify_test", "title": "verify_demo"}})
// 响应值断言
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if result.GetCeilInt64("id") <= 0 {
return fmt.Errorf("返回的 id 必须大于 0, 实际 %d", result.GetCeilInt64("id"))
}
if result.GetString("name") != name {
return fmt.Errorf("name 期望 '%s', 实际 '%s'", name, result.GetString("name"))
}
// 数据库状态校验
row := a.DB().Get("test_batch", "*", Map{"AND": Map{"name": name, "title": "verify_demo"}})
if row == nil {
return fmt.Errorf("test_batch 表未找到 name=verify_test 的记录")
return fmt.Errorf("test_batch 表未找到 name=%s 的记录", name)
}
if row.GetCeilInt64("state") != 0 {
return fmt.Errorf("test_batch.state 期望 0, 实际 %d", row.GetCeilInt64("state"))
}
// 校验之前插入的记录仍然存在(验证不会误删)
existing := a.DB().Get("test_batch", "*", Map{"AND": Map{"name": "existing_record", "title": "verify_demo"}})
if existing == nil {
return fmt.Errorf("之前插入的 existing_record 丢失")
}
return nil
}).
Post("写入并校验DB-正确", 0, Map{"id": int64(1), "name": "sample"})
Post("写入并校验DB+响应", 0, Map{"id": int64(1), "name": "sample"})
// ======== 演示故意失败的 Verify ========
a.Form(Map{"name": "verify_fail"}).
Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if result.GetCeilInt64("id") <= 0 {
return fmt.Errorf("返回的 id 必须大于 0, 实际 %d", result.GetCeilInt64("id"))
}
row := a.DB().Get("test_batch", "*", Map{"AND": Map{"name": "verify_fail", "title": "verify_demo"}})
if row == nil {
return fmt.Errorf("test_batch 表未找到 name=verify_fail 的记录")
}
if row.GetCeilInt64("state") != 99 {
return fmt.Errorf("test_batch.state 期望 99, 实际 %d(接口写入的是0,这里故意校验99演示失败效果)", row.GetCeilInt64("state"))
return fmt.Errorf("test_batch.state 期望 99, 实际 %d(接口写入 0故意校验 99 演示失败效果)", row.GetCeilInt64("state"))
}
return nil
}).
+81 -3
View File
@@ -1,12 +1,90 @@
package app
import (
"fmt"
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
var MysqlTest = CtrTest{
"tables": {Desc: "查询 MySQL 所有表", Func: func(a *Api) { a.Get("SHOW TABLES", 0) }},
"describe": {Desc: "查询 MySQL 表结构", Func: func(a *Api) { a.Query(Map{"table": "admin"}).Get("DESCRIBE admin", 0) }},
"rawsql": {Desc: "MySQL 原生 SQL 测试", Func: func(a *Api) { a.Get("MySQL原生SQL", 0) }},
"tables": {Desc: "查询 MySQL 所有表", Func: func(a *Api) {
if a.DB().Type != "mysql" {
return
}
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
tables := result.GetSlice("tables")
if tables == nil {
return fmt.Errorf("tables 字段缺失")
}
if len(tables) == 0 {
return fmt.Errorf("MySQL 表列表为空,至少应有测试表")
}
return nil
}).Get("SHOW TABLES", 0, Map{
"tables": Slice{},
})
}},
"describe": {Desc: "查询 MySQL 表结构", Func: func(a *Api) {
if a.DB().Type != "mysql" {
return
}
// ======== 错误用例 ========
a.Get("不传table参数", 1, "请提供 table 参数")
// ======== 正确请求 + 结构校验 + Verify ========
a.Query(Map{"table": "admin"}).
Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if result.GetString("table") != "admin" {
return fmt.Errorf("table 期望 'admin', 实际 '%s'", result.GetString("table"))
}
columns := result.GetSlice("columns")
if len(columns) == 0 {
return fmt.Errorf("columns 为空,admin 表应有字段定义")
}
return nil
}).
Get("DESCRIBE admin", 0, Map{
"table": "sample", "columns": Slice{}, "sample_data": Slice{},
})
}},
"rawsql": {Desc: "MySQL 原生 SQL 测试", Func: func(a *Api) {
if a.DB().Type != "mysql" {
return
}
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if !result.GetBool("success") {
return fmt.Errorf("MySQL 原生 SQL 测试未通过")
}
tests := result.GetSlice("tests")
if len(tests) == 0 {
return fmt.Errorf("tests 数组为空")
}
for i := 0; i < len(tests); i++ {
item := tests.GetMap(i)
if item != nil && !item.GetBool("result") {
return fmt.Errorf("子项 '%s' 失败", item.GetString("name"))
}
}
return nil
}).Get("MySQL原生SQL", 0, Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample", "result": true}},
})
}},
}
+173 -10
View File
@@ -1,18 +1,181 @@
package app
import (
"fmt"
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
var TestTest = CtrTest{
"all": {Desc: "运行全部通用测试", Func: func(a *Api) { a.Get("全部测试", 0) }},
"crud": {Desc: "基础 CRUD 测试", Func: func(a *Api) { a.Get("CRUD测试", 0) }},
"condition": {Desc: "条件查询语法测试", Func: func(a *Api) { a.Get("条件查询", 0) }},
"chain": {Desc: "链式查询测试", Func: func(a *Api) { a.Get("链式查询", 0) }},
"join": {Desc: "JOIN 查询测试", Func: func(a *Api) { a.Get("JOIN查询", 0) }},
"aggregate": {Desc: "聚合函数测试", Func: func(a *Api) { a.Get("聚合函数", 0) }},
"pagination": {Desc: "分页查询测试", Func: func(a *Api) { a.Get("分页查询", 0) }},
"batch": {Desc: "批量插入测试", Func: func(a *Api) { a.Get("批量插入", 0) }},
"upsert": {Desc: "Upsert 测试", Func: func(a *Api) { a.Get("Upsert测试", 0) }},
"transaction": {Desc: "事务测试", Func: func(a *Api) { a.Get("事务测试", 0) }},
// ======== 错误接口测试 ========
"test": {Desc: "固定错误返回", Func: func(a *Api) {
a.Get("固定返回错误码2", 2, "dsadasd")
}},
// ======== ORM 全量测试 ========
"all": {Desc: "运行全部通用测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
subTests := []string{
"1_basic_crud", "2_condition_syntax", "3_chain_query",
"4_join_query", "5_aggregate", "6_pagination",
"7_batch_insert", "8_upsert", "9_transaction",
}
for _, key := range subTests {
sub := result.GetMap(key)
if sub == nil {
return fmt.Errorf("子测试 %s 缺失", key)
}
if sub.GetString("name") == "" {
return fmt.Errorf("子测试 %s 缺少 name 字段", key)
}
tests := sub.GetSlice("tests")
if len(tests) == 0 {
return fmt.Errorf("子测试 %s 的 tests 数组为空", key)
}
}
return nil
}).Get("全部测试", 0, Map{
"1_basic_crud": Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample", "result": true}},
},
"2_condition_syntax": Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample", "result": true}},
},
"3_chain_query": Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
},
"4_join_query": Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
},
"5_aggregate": Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
},
"6_pagination": Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
},
"7_batch_insert": Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
},
"8_upsert": Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
},
"9_transaction": Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
},
})
}},
// ======== 单项 ORM 测试 ========
"crud": {Desc: "基础 CRUD 测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
return verifyOrmSubTest(a, "基础CRUD测试")
}).Get("CRUD测试", 0, Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample", "result": true}},
})
}},
"condition": {Desc: "条件查询语法测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
return verifyOrmSubTest(a, "条件查询语法测试")
}).Get("条件查询", 0, Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample", "result": true}},
})
}},
"chain": {Desc: "链式查询测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
return verifyOrmSubTest(a, "链式查询测试")
}).Get("链式查询", 0, Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
})
}},
"join": {Desc: "JOIN 查询测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
return verifyOrmSubTest(a, "JOIN查询测试")
}).Get("JOIN查询", 0, Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
})
}},
"aggregate": {Desc: "聚合函数测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
return verifyOrmSubTest(a, "聚合函数测试")
}).Get("聚合函数", 0, Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
})
}},
"pagination": {Desc: "分页查询测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
return verifyOrmSubTest(a, "分页查询测试")
}).Get("分页查询", 0, Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
})
}},
"batch": {Desc: "批量插入测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
return verifyOrmSubTest(a, "批量插入测试")
}).Get("批量插入", 0, Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
})
}},
"upsert": {Desc: "Upsert 测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
return verifyOrmSubTest(a, "Upsert测试")
}).Get("Upsert测试", 0, Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
})
}},
"transaction": {Desc: "事务测试", Func: func(a *Api) {
a.Verify(func(a *Api) error {
return verifyOrmSubTest(a, "事务测试")
}).Get("事务测试", 0, Map{
"name": "sample", "success": true,
"tests": Slice{Map{"name": "sample"}},
})
}},
}
// verifyOrmSubTest 校验 ORM 自测端点的结构完整性
// 注意:这些 handler 是诊断工具,某些子项(如 table.column 格式聚合)在特定数据库下
// 预期 result=false,因此不逐项检查 result 标志,只验证结构
func verifyOrmSubTest(a *Api, testName string) error {
result := a.Resp().GetBody().GetMap("result")
if result == nil {
return fmt.Errorf("result 为 nil")
}
if result.GetString("name") == "" {
return fmt.Errorf("%s 缺少 name 字段", testName)
}
tests := result.GetSlice("tests")
if len(tests) == 0 {
return fmt.Errorf("%s tests 数组为空", testName)
}
return nil
}
File diff suppressed because it is too large Load Diff