feat(db): 添加对达梦数据库的支持
- 在应用程序中新增对达梦数据库(DM)的配置和连接支持 - 实现 SetDmDB 函数以配置达梦数据库连接 - 更新数据库操作逻辑,支持达梦特有的 SQL 语法和功能 - 在相关文件中添加达梦数据库的处理逻辑,包括表创建、数据插入和查询 - 更新 go.mod 和 go.sum 文件以引入达梦数据库驱动 - 增强文档,详细说明达梦数据库的配置和使用方法
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
)
|
||||
|
||||
var ProjectTest = ProjTest{
|
||||
"test": TestTest,
|
||||
"mysql": MysqlTest,
|
||||
"dmdb": DmdbTest,
|
||||
"cache": CacheTest,
|
||||
}
|
||||
|
||||
var testApp *TestApp
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Go test 的 CWD 是包目录 example/app/,切回 example/ 使相对路径正确
|
||||
_ = os.Chdir("..")
|
||||
|
||||
testApp = NewTestApp("config/config.json",
|
||||
TestProj{
|
||||
"app": {
|
||||
Proj: Project,
|
||||
Tests: ProjectTest,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// 在测试前初始化各数据库的测试表和种子数据
|
||||
SetupDatabase(&testApp.Db)
|
||||
|
||||
code := m.Run()
|
||||
testApp.PrintCoverage()
|
||||
testApp.GenerateSwagger("My API", "1.0.0", testApp.Config.GetString("tpt"))
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func TestApi(t *testing.T) {
|
||||
testApp.RunTests(t)
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
)
|
||||
|
||||
// CacheCtr 缓存测试控制器(数据库无关)
|
||||
var CacheCtr = Ctr{
|
||||
"all": func(that *Context) { that.Display(0, testCacheAll(that)) },
|
||||
"compat": func(that *Context) { that.Display(0, testCacheCompatible(that)) },
|
||||
"batch": func(that *Context) {
|
||||
that.Display(0, Map{"message": "批量缓存测试完成,请查看控制台输出和日志文件"})
|
||||
},
|
||||
}
|
||||
|
||||
func testCacheAll(that *Context) Map {
|
||||
result := Map{"name": "数据库缓存测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
|
||||
cacheMode := "unknown"
|
||||
if that.Application.HoTimeCache != nil && that.Application.HoTimeCache.Config != nil {
|
||||
dbConfig := that.Application.HoTimeCache.Config.GetMap("db")
|
||||
if dbConfig != nil {
|
||||
cacheMode = dbConfig.GetString("mode")
|
||||
if cacheMode == "" {
|
||||
cacheMode = "new"
|
||||
}
|
||||
}
|
||||
}
|
||||
result["cache_mode"] = cacheMode
|
||||
|
||||
testPrefix := fmt.Sprintf("cache_test_%d_", time.Now().UnixNano())
|
||||
|
||||
// 1. 基础读写测试
|
||||
test1 := Map{"name": "1. 基础 set/get 测试"}
|
||||
testKey1 := testPrefix + "basic"
|
||||
testValue1 := Map{"name": "测试数据", "count": 123, "active": true}
|
||||
that.Application.Cache(testKey1, testValue1)
|
||||
cached1 := that.Application.Cache(testKey1)
|
||||
if cached1.Data != nil {
|
||||
cachedMap := cached1.ToMap()
|
||||
test1["result"] = cachedMap.GetString("name") == "测试数据" && cachedMap.GetInt("count") == 123
|
||||
test1["cached_value"] = cachedMap
|
||||
} else {
|
||||
test1["result"] = false
|
||||
test1["error"] = "缓存读取返回 nil"
|
||||
}
|
||||
tests = append(tests, test1)
|
||||
|
||||
// 2. 删除缓存测试
|
||||
test2 := Map{"name": "2. delete 删除缓存测试"}
|
||||
testKey2 := testPrefix + "delete"
|
||||
that.Application.Cache(testKey2, "删除测试值")
|
||||
before := that.Application.Cache(testKey2)
|
||||
beforeExists := before.Data != nil
|
||||
that.Application.Cache(testKey2, nil)
|
||||
after := that.Application.Cache(testKey2)
|
||||
afterExists := after.Data != nil
|
||||
test2["result"] = beforeExists && !afterExists
|
||||
test2["before_exists"] = beforeExists
|
||||
test2["after_exists"] = afterExists
|
||||
tests = append(tests, test2)
|
||||
|
||||
// 3. 过期时间测试
|
||||
test3 := Map{"name": "3. 过期时间测试(短超时)"}
|
||||
testKey3 := testPrefix + "expire"
|
||||
that.Application.Cache(testKey3, "短期数据", 2)
|
||||
immediate := that.Application.Cache(testKey3)
|
||||
immediateExists := immediate.Data != nil
|
||||
test3["result"] = immediateExists
|
||||
test3["immediate_exists"] = immediateExists
|
||||
test3["note"] = "设置了2秒过期,可等待后再次访问验证过期"
|
||||
tests = append(tests, test3)
|
||||
|
||||
// 4. 不存在的 key 读取测试
|
||||
test4 := Map{"name": "4. 不存在的 key 读取测试"}
|
||||
nonExistKey := testPrefix + "non_exist_key_" + fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
nonExist := that.Application.Cache(nonExistKey)
|
||||
test4["result"] = nonExist.Data == nil
|
||||
test4["value"] = nonExist.Data
|
||||
tests = append(tests, test4)
|
||||
|
||||
// 5. 重复 set 同一个 key 测试
|
||||
test5 := Map{"name": "5. 重复 set 同一个 key 测试"}
|
||||
testKey5 := testPrefix + "repeat"
|
||||
that.Application.Cache(testKey5, "第一次值")
|
||||
first := that.Application.Cache(testKey5).ToStr()
|
||||
that.Application.Cache(testKey5, "第二次值")
|
||||
second := that.Application.Cache(testKey5).ToStr()
|
||||
that.Application.Cache(testKey5, Map{"version": 3})
|
||||
third := that.Application.Cache(testKey5).ToMap()
|
||||
test5["result"] = first == "第一次值" && second == "第二次值" && third.GetInt("version") == 3
|
||||
test5["first"] = first
|
||||
test5["second"] = second
|
||||
test5["third"] = third
|
||||
tests = append(tests, test5)
|
||||
|
||||
// 6. 通配删除测试
|
||||
test6 := Map{"name": "6. 通配删除测试 (key*)"}
|
||||
wildcardPrefix := testPrefix + "wildcard_"
|
||||
that.Application.Cache(wildcardPrefix+"a", "值A")
|
||||
that.Application.Cache(wildcardPrefix+"b", "值B")
|
||||
that.Application.Cache(wildcardPrefix+"c", "值C")
|
||||
aExists := that.Application.Cache(wildcardPrefix+"a").Data != nil
|
||||
bExists := that.Application.Cache(wildcardPrefix+"b").Data != nil
|
||||
cExists := that.Application.Cache(wildcardPrefix+"c").Data != nil
|
||||
allExistBefore := aExists && bExists && cExists
|
||||
that.Application.Cache(wildcardPrefix+"*", nil)
|
||||
aAfter := that.Application.Cache(wildcardPrefix+"a").Data != nil
|
||||
bAfter := that.Application.Cache(wildcardPrefix+"b").Data != nil
|
||||
cAfter := that.Application.Cache(wildcardPrefix+"c").Data != nil
|
||||
allDeletedAfter := !aAfter && !bAfter && !cAfter
|
||||
test6["result"] = allExistBefore && allDeletedAfter
|
||||
test6["before"] = Map{"a": aExists, "b": bExists, "c": cExists}
|
||||
test6["after"] = Map{"a": aAfter, "b": bAfter, "c": cAfter}
|
||||
tests = append(tests, test6)
|
||||
|
||||
// 7. 不同数据类型测试
|
||||
test7 := Map{"name": "7. 不同数据类型存储测试"}
|
||||
that.Application.Cache(testPrefix+"type_string", "字符串值")
|
||||
typeString := that.Application.Cache(testPrefix + "type_string").ToStr()
|
||||
that.Application.Cache(testPrefix+"type_int", 12345)
|
||||
typeInt := that.Application.Cache(testPrefix + "type_int").ToInt()
|
||||
that.Application.Cache(testPrefix+"type_float", 3.14159)
|
||||
typeFloat := that.Application.Cache(testPrefix + "type_float").ToFloat64()
|
||||
that.Application.Cache(testPrefix+"type_bool", true)
|
||||
typeBoolData := that.Application.Cache(testPrefix + "type_bool").Data
|
||||
typeBool := typeBoolData == true || typeBoolData == "true" || typeBoolData == 1.0
|
||||
that.Application.Cache(testPrefix+"type_map", Map{"key": "value", "num": 100})
|
||||
typeMap := that.Application.Cache(testPrefix + "type_map").ToMap()
|
||||
that.Application.Cache(testPrefix+"type_slice", Slice{1, 2, 3, "four", Map{"five": 5}})
|
||||
typeSlice := that.Application.Cache(testPrefix + "type_slice").ToSlice()
|
||||
test7["result"] = typeString == "字符串值" &&
|
||||
typeInt == 12345 &&
|
||||
typeFloat > 3.14 && typeFloat < 3.15 &&
|
||||
typeBool == true &&
|
||||
typeMap.GetString("key") == "value" &&
|
||||
len(typeSlice) == 5
|
||||
test7["string"] = typeString
|
||||
test7["int"] = typeInt
|
||||
test7["float"] = typeFloat
|
||||
test7["bool"] = typeBool
|
||||
test7["map"] = typeMap
|
||||
test7["slice"] = typeSlice
|
||||
tests = append(tests, test7)
|
||||
|
||||
// 8. 自定义超时时间测试
|
||||
test8 := Map{"name": "8. 自定义超时时间参数测试"}
|
||||
testKey8 := testPrefix + "custom_timeout"
|
||||
that.Application.Cache(testKey8, "长期数据", 3600)
|
||||
longTerm := that.Application.Cache(testKey8)
|
||||
test8["result"] = longTerm.Data != nil
|
||||
test8["value"] = longTerm.ToStr()
|
||||
tests = append(tests, test8)
|
||||
|
||||
// 9. 查询缓存表状态
|
||||
test9 := Map{"name": "9. 缓存表状态查询"}
|
||||
prefix := that.Db.GetPrefix()
|
||||
newTableName := prefix + "hotime_cache"
|
||||
newCount := that.Db.Count(newTableName)
|
||||
test9["new_table_count"] = newCount
|
||||
test9["new_table_name"] = newTableName
|
||||
test9["result"] = newCount >= 0
|
||||
tests = append(tests, test9)
|
||||
|
||||
that.Application.Cache(testPrefix+"*", nil)
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
result["cleanup"] = "已清理所有测试缓存数据"
|
||||
return result
|
||||
}
|
||||
|
||||
func testCacheCompatible(that *Context) Map {
|
||||
result := Map{
|
||||
"test_name": "兼容模式白盒测试",
|
||||
"timestamp": time.Now().Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
|
||||
prefix := that.Db.GetPrefix()
|
||||
newTableName := prefix + "hotime_cache"
|
||||
legacyTableName := prefix + "cached"
|
||||
|
||||
tests := Slice{}
|
||||
|
||||
test1 := Map{"name": "1. 查询当前缓存模式"}
|
||||
cacheConfig := that.Application.Config.GetMap("cache")
|
||||
dbConfig := cacheConfig.GetMap("db")
|
||||
mode := dbConfig.GetString("mode")
|
||||
if mode == "" {
|
||||
mode = "默认(compatible)"
|
||||
}
|
||||
test1["mode"] = mode
|
||||
test1["result"] = true
|
||||
tests = append(tests, test1)
|
||||
|
||||
// 根据数据库类型使用不同的引号
|
||||
q := "`"
|
||||
if that.Db.Dialect != nil {
|
||||
q = that.Db.Dialect.QuoteChar()
|
||||
}
|
||||
|
||||
test2 := Map{"name": "2. 查询老表cached现有数据"}
|
||||
legacyData := that.Db.Query("SELECT * FROM " + q + legacyTableName + q + " LIMIT 5")
|
||||
test2["legacy_table"] = legacyTableName
|
||||
test2["count"] = len(legacyData)
|
||||
test2["data"] = legacyData
|
||||
test2["result"] = true
|
||||
tests = append(tests, test2)
|
||||
|
||||
test3 := Map{"name": "3. 查询新表hotime_cache现有数据"}
|
||||
newData := that.Db.Query("SELECT * FROM " + q + newTableName + q + " LIMIT 5")
|
||||
test3["new_table"] = newTableName
|
||||
test3["count"] = len(newData)
|
||||
test3["data"] = newData
|
||||
test3["result"] = true
|
||||
tests = append(tests, test3)
|
||||
|
||||
test4 := Map{"name": "4. 测试兼容模式老表回退读取"}
|
||||
testKey4 := "test_compat_fallback_" + ObjToStr(time.Now().UnixNano())
|
||||
testValue4 := Map{"admin_id": 999, "admin_name": "测试老数据"}
|
||||
testValueJson4 := ObjToStr(Map{"data": testValue4})
|
||||
that.Db.Insert(legacyTableName, Map{
|
||||
"key": testKey4,
|
||||
"value": testValueJson4,
|
||||
"endtime": time.Now().Unix() + 3600,
|
||||
"time": time.Now().UnixNano(),
|
||||
})
|
||||
test4["test_key"] = testKey4
|
||||
newExists := that.Db.Get(newTableName, "*", Map{"key": testKey4})
|
||||
test4["key_in_new_table"] = newExists != nil
|
||||
cacheValue := that.Application.Cache(testKey4)
|
||||
test4["cache_api_result"] = cacheValue.Data
|
||||
test4["result"] = newExists == nil && cacheValue.Data != nil
|
||||
tests = append(tests, test4)
|
||||
|
||||
test5 := Map{"name": "5. 测试兼容模式写新删老"}
|
||||
testKey5 := "test_compat_write_" + ObjToStr(time.Now().UnixNano())
|
||||
testValue5 := "兼容模式测试数据"
|
||||
that.Db.Insert(legacyTableName, Map{
|
||||
"key": testKey5,
|
||||
"value": `{"data":"老表原始数据"}`,
|
||||
"endtime": time.Now().Unix() + 3600,
|
||||
"time": time.Now().UnixNano(),
|
||||
})
|
||||
legacyBefore := that.Db.Get(legacyTableName, "*", Map{"key": testKey5})
|
||||
test5["step1_legacy_before"] = legacyBefore != nil
|
||||
that.Application.Cache(testKey5, testValue5)
|
||||
newAfter := that.Db.Get(newTableName, "*", Map{"key": testKey5})
|
||||
test5["step2_new_after"] = newAfter != nil
|
||||
if newAfter != nil {
|
||||
test5["new_value"] = newAfter.GetString("value")
|
||||
}
|
||||
legacyAfter := that.Db.Get(legacyTableName, "*", Map{"key": testKey5})
|
||||
test5["step3_legacy_after_deleted"] = legacyAfter == nil
|
||||
test5["result"] = legacyBefore != nil && newAfter != nil && legacyAfter == nil
|
||||
tests = append(tests, test5)
|
||||
|
||||
test6 := Map{"name": "6. 测试兼容模式删除(删除两表)"}
|
||||
testKey6 := "test_compat_delete_" + ObjToStr(time.Now().UnixNano())
|
||||
that.Db.Insert(legacyTableName, Map{
|
||||
"key": testKey6,
|
||||
"value": `{"data":"待删除老数据"}`,
|
||||
"endtime": time.Now().Unix() + 3600,
|
||||
"time": time.Now().UnixNano(),
|
||||
})
|
||||
that.Db.Insert(newTableName, Map{
|
||||
"key": testKey6,
|
||||
"value": `"待删除新数据"`,
|
||||
"end_time": time.Now().Add(time.Hour).Format("2006-01-02 15:04:05"),
|
||||
"state": 0,
|
||||
"create_time": time.Now().Format("2006-01-02 15:04:05"),
|
||||
"modify_time": time.Now().Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
test6["before_legacy"] = that.Db.Get(legacyTableName, "*", Map{"key": testKey6}) != nil
|
||||
test6["before_new"] = that.Db.Get(newTableName, "*", Map{"key": testKey6}) != nil
|
||||
that.Application.Cache(testKey6, nil)
|
||||
test6["after_legacy_deleted"] = that.Db.Get(legacyTableName, "*", Map{"key": testKey6}) == nil
|
||||
test6["after_new_deleted"] = that.Db.Get(newTableName, "*", Map{"key": testKey6}) == nil
|
||||
test6["result"] = test6.GetBool("before_legacy") && test6.GetBool("before_new") &&
|
||||
test6.GetBool("after_legacy_deleted") && test6.GetBool("after_new_deleted")
|
||||
tests = append(tests, test6)
|
||||
|
||||
that.Db.Delete(newTableName, Map{"key[~]": "test_compat_%"})
|
||||
that.Db.Delete(legacyTableName, Map{"key[~]": "test_compat_%"})
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
)
|
||||
|
||||
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) }},
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
)
|
||||
|
||||
// DmdbCtr 达梦数据库特有测试控制器
|
||||
var DmdbCtr = Ctr{
|
||||
"tables": func(that *Context) {
|
||||
if that.Db.Type != "dm" && that.Db.Type != "dameng" {
|
||||
that.Display(0, Map{"skip": true, "reason": "当前非达梦数据库"})
|
||||
return
|
||||
}
|
||||
tables := that.Db.Query(`SELECT TABLE_NAME AS "name", COMMENTS AS "label" FROM USER_TAB_COMMENTS WHERE TABLE_TYPE='TABLE'`)
|
||||
that.Display(0, Map{"tables": tables})
|
||||
},
|
||||
|
||||
"describe": func(that *Context) {
|
||||
if that.Db.Type != "dm" && that.Db.Type != "dameng" {
|
||||
that.Display(0, Map{"skip": true, "reason": "当前非达梦数据库"})
|
||||
return
|
||||
}
|
||||
tableName := that.Req.FormValue("table")
|
||||
if tableName == "" {
|
||||
that.Display(1, "请提供 table 参数")
|
||||
return
|
||||
}
|
||||
columns := that.Db.Query(`SELECT c.COLUMN_NAME AS "name", c.DATA_TYPE AS "type", m.COMMENTS AS "label", c.NULLABLE AS "must" FROM USER_TAB_COLUMNS c LEFT JOIN USER_COL_COMMENTS m ON c.TABLE_NAME=m.TABLE_NAME AND c.COLUMN_NAME=m.COLUMN_NAME WHERE c.TABLE_NAME='` + tableName + `' ORDER BY c.COLUMN_ID`)
|
||||
data := that.Db.Select(tableName, "*", Map{"LIMIT": 10})
|
||||
that.Display(0, Map{"table": tableName, "columns": columns, "sample_data": data})
|
||||
},
|
||||
|
||||
"rawsql": func(that *Context) {
|
||||
if that.Db.Type != "dm" && that.Db.Type != "dameng" {
|
||||
that.Display(0, Map{"skip": true, "reason": "当前非达梦数据库"})
|
||||
return
|
||||
}
|
||||
result := testDmRawSQL(that)
|
||||
that.Display(0, result)
|
||||
},
|
||||
}
|
||||
|
||||
func testDmRawSQL(that *Context) Map {
|
||||
result := Map{"name": "达梦原生SQL测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
|
||||
test1 := Map{"name": "Query 原生查询 (双引号)"}
|
||||
articles1 := that.Db.Query(`SELECT "id", "title", "author" FROM "article" WHERE "state" = ? LIMIT ?`, 0, 5)
|
||||
test1["result"] = len(articles1) >= 0
|
||||
test1["count"] = len(articles1)
|
||||
tests = append(tests, test1)
|
||||
|
||||
test2 := Map{"name": "Exec 原生执行 (双引号)"}
|
||||
testArticle := that.Db.Get("article", "id", Map{"state": 0})
|
||||
if testArticle != nil {
|
||||
res, err := that.Db.Exec(`UPDATE "article" SET "modify_time" = NOW() WHERE "id" = ?`, testArticle.GetInt64("id"))
|
||||
if err.GetError() == nil && res != nil {
|
||||
affected, _ := res.RowsAffected()
|
||||
test2["result"] = affected >= 0
|
||||
test2["affected"] = affected
|
||||
} else {
|
||||
test2["result"] = false
|
||||
test2["error"] = err.GetError()
|
||||
}
|
||||
} else {
|
||||
test2["result"] = true
|
||||
test2["note"] = "无可用测试数据"
|
||||
}
|
||||
tests = append(tests, test2)
|
||||
|
||||
test3 := Map{"name": "USER_TABLES 查询"}
|
||||
tables := that.Db.Query(`SELECT TABLE_NAME FROM USER_TABLES`)
|
||||
test3["result"] = len(tables) >= 0
|
||||
test3["count"] = len(tables)
|
||||
tests = append(tests, test3)
|
||||
|
||||
test4 := Map{"name": "USER_TAB_COLUMNS 查询"}
|
||||
cols := that.Db.Query(`SELECT COLUMN_NAME, DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME='admin' ORDER BY COLUMN_ID`)
|
||||
test4["result"] = len(cols) >= 0
|
||||
test4["count"] = len(cols)
|
||||
test4["columns"] = cols
|
||||
tests = append(tests, test4)
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "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) }},
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
)
|
||||
|
||||
var Project = Proj{
|
||||
"test": TestCtr,
|
||||
"mysql": MysqlCtr,
|
||||
"dmdb": DmdbCtr,
|
||||
"cache": CacheCtr,
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
)
|
||||
|
||||
// MysqlCtr MySQL 特有测试控制器
|
||||
var MysqlCtr = Ctr{
|
||||
"tables": func(that *Context) {
|
||||
if that.Db.Type != "mysql" {
|
||||
that.Display(0, Map{"skip": true, "reason": "当前非 MySQL 数据库"})
|
||||
return
|
||||
}
|
||||
tables := that.Db.Query("SHOW TABLES")
|
||||
that.Display(0, Map{"tables": tables})
|
||||
},
|
||||
|
||||
"describe": func(that *Context) {
|
||||
if that.Db.Type != "mysql" {
|
||||
that.Display(0, Map{"skip": true, "reason": "当前非 MySQL 数据库"})
|
||||
return
|
||||
}
|
||||
tableName := that.Req.FormValue("table")
|
||||
if tableName == "" {
|
||||
that.Display(1, "请提供 table 参数")
|
||||
return
|
||||
}
|
||||
columns := that.Db.Query("DESCRIBE " + tableName)
|
||||
data := that.Db.Select(tableName, Map{"LIMIT": 10})
|
||||
that.Display(0, Map{"table": tableName, "columns": columns, "sample_data": data})
|
||||
},
|
||||
|
||||
"rawsql": func(that *Context) {
|
||||
if that.Db.Type != "mysql" {
|
||||
that.Display(0, Map{"skip": true, "reason": "当前非 MySQL 数据库"})
|
||||
return
|
||||
}
|
||||
result := testMysqlRawSQL(that)
|
||||
that.Display(0, result)
|
||||
},
|
||||
}
|
||||
|
||||
func testMysqlRawSQL(that *Context) Map {
|
||||
result := Map{"name": "MySQL原生SQL测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
|
||||
test1 := Map{"name": "Query 原生查询 (反引号)"}
|
||||
articles1 := that.Db.Query("SELECT id, title, author FROM `article` WHERE state = ? LIMIT ?", 0, 5)
|
||||
test1["result"] = len(articles1) >= 0
|
||||
test1["count"] = len(articles1)
|
||||
tests = append(tests, test1)
|
||||
|
||||
test2 := Map{"name": "Exec 原生执行 (反引号)"}
|
||||
testArticle := that.Db.Get("article", "id", Map{"state": 0})
|
||||
if testArticle != nil {
|
||||
res, err := that.Db.Exec("UPDATE `article` SET modify_time = NOW() WHERE id = ?", testArticle.GetInt64("id"))
|
||||
if err.GetError() == nil && res != nil {
|
||||
affected, _ := res.RowsAffected()
|
||||
test2["result"] = affected >= 0
|
||||
test2["affected"] = affected
|
||||
} else {
|
||||
test2["result"] = false
|
||||
test2["error"] = err.GetError()
|
||||
}
|
||||
} else {
|
||||
test2["result"] = true
|
||||
test2["note"] = "无可用测试数据"
|
||||
}
|
||||
tests = append(tests, test2)
|
||||
|
||||
test3 := Map{"name": "IN (?) 非空数组展开"}
|
||||
articles3 := that.Db.Query("SELECT id, title FROM `article` WHERE id IN (?) LIMIT 10", []int{1, 2, 3, 4, 5})
|
||||
test3["result"] = len(articles3) >= 0
|
||||
test3["count"] = len(articles3)
|
||||
test3["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test3)
|
||||
|
||||
test4 := Map{"name": "IN (?) 空数组替换为1=0"}
|
||||
articles4 := that.Db.Query("SELECT id, title FROM `article` WHERE id IN (?) LIMIT 10", []int{})
|
||||
test4["result"] = len(articles4) == 0
|
||||
test4["count"] = len(articles4)
|
||||
test4["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test4)
|
||||
|
||||
test5 := Map{"name": "NOT IN (?) 空数组替换为1=1"}
|
||||
articles5 := that.Db.Query("SELECT id, title FROM `article` WHERE id NOT IN (?) LIMIT 10", []int{})
|
||||
test5["result"] = len(articles5) > 0
|
||||
test5["count"] = len(articles5)
|
||||
test5["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test5)
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "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) }},
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "code.hoteas.com/golang/hotime/db"
|
||||
)
|
||||
|
||||
// SetupDMDatabase 为达梦数据库创建测试表并灌入测试数据
|
||||
func SetupDMDatabase(db *HoTimeDB) {
|
||||
if db.Type != "dm" && db.Type != "dameng" {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("[DM Setup] 开始初始化达梦数据库测试环境...")
|
||||
|
||||
prefix := db.GetPrefix()
|
||||
createDMTables(db, prefix)
|
||||
seedTestData(db, "DM")
|
||||
|
||||
fmt.Println("[DM Setup] 达梦数据库测试环境初始化完成")
|
||||
}
|
||||
|
||||
func dmTableExists(db *HoTimeDB, tableName string) bool {
|
||||
// COUNT(*) 即使表为空也会返回一行;若表不存在则报错返回空切片。
|
||||
// 相比 USER_TABLES,这里直接检测当前 schema 下表的可访问性,避免
|
||||
// 连接 schema(?schema=TEST)与 USER_TABLES(SYSDBA 自身 schema)错配。
|
||||
res := db.Query(`SELECT COUNT(*) as cnt FROM "` + tableName + `"`)
|
||||
return len(res) > 0
|
||||
}
|
||||
|
||||
func createDMTables(db *HoTimeDB, prefix string) {
|
||||
// admin 表
|
||||
tbl := prefix + "admin"
|
||||
if !dmTableExists(db, tbl) {
|
||||
fmt.Println("[DM Setup] 创建表:", tbl)
|
||||
db.Exec(`CREATE TABLE "` + tbl + `" (
|
||||
"id" INT IDENTITY(1,1) PRIMARY KEY,
|
||||
"name" VARCHAR(100),
|
||||
"phone" VARCHAR(20),
|
||||
"state" INT DEFAULT 0,
|
||||
"password" VARCHAR(100),
|
||||
"role_id" INT DEFAULT 0,
|
||||
"title" VARCHAR(100),
|
||||
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
"modify_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)`)
|
||||
db.Exec(`CREATE UNIQUE INDEX "uk_` + tbl + `_phone" ON "` + tbl + `" ("phone")`)
|
||||
}
|
||||
|
||||
// ctg 分类表
|
||||
tbl = prefix + "ctg"
|
||||
if !dmTableExists(db, tbl) {
|
||||
fmt.Println("[DM Setup] 创建表:", tbl)
|
||||
db.Exec(`CREATE TABLE "` + tbl + `" (
|
||||
"id" INT IDENTITY(1,1) PRIMARY KEY,
|
||||
"name" VARCHAR(100),
|
||||
"state" INT DEFAULT 0,
|
||||
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
"modify_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)`)
|
||||
}
|
||||
|
||||
// article 文章表
|
||||
tbl = prefix + "article"
|
||||
if !dmTableExists(db, tbl) {
|
||||
fmt.Println("[DM Setup] 创建表:", tbl)
|
||||
db.Exec(`CREATE TABLE "` + tbl + `" (
|
||||
"id" INT IDENTITY(1,1) PRIMARY KEY,
|
||||
"title" VARCHAR(200),
|
||||
"author" VARCHAR(100),
|
||||
"content" CLOB,
|
||||
"state" INT DEFAULT 0,
|
||||
"click_num" INT DEFAULT 0,
|
||||
"sort" INT DEFAULT 0,
|
||||
"img" VARCHAR(500),
|
||||
"ctg_id" INT DEFAULT 0,
|
||||
"admin_id" INT DEFAULT 0,
|
||||
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
"modify_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)`)
|
||||
}
|
||||
|
||||
// test_batch 测试批量表
|
||||
tbl = prefix + "test_batch"
|
||||
if !dmTableExists(db, tbl) {
|
||||
fmt.Println("[DM Setup] 创建表:", tbl)
|
||||
db.Exec(`CREATE TABLE "` + tbl + `" (
|
||||
"id" INT IDENTITY(1,1) PRIMARY KEY,
|
||||
"name" VARCHAR(100),
|
||||
"title" VARCHAR(200),
|
||||
"state" INT DEFAULT 0,
|
||||
"create_time" TIMESTAMP
|
||||
)`)
|
||||
}
|
||||
|
||||
// cached 老版本缓存表(兼容模式测试需要)
|
||||
tbl = prefix + "cached"
|
||||
if !dmTableExists(db, tbl) {
|
||||
fmt.Println("[DM Setup] 创建表:", tbl)
|
||||
db.Exec(`CREATE TABLE "` + tbl + `" (
|
||||
"id" INT IDENTITY(1,1) PRIMARY KEY,
|
||||
"key" VARCHAR(64),
|
||||
"value" CLOB,
|
||||
"endtime" BIGINT,
|
||||
"time" BIGINT
|
||||
)`)
|
||||
}
|
||||
|
||||
// hotime_cache 新版缓存表
|
||||
tbl = prefix + "hotime_cache"
|
||||
if !dmTableExists(db, tbl) {
|
||||
fmt.Println("[DM Setup] 创建表:", tbl)
|
||||
db.Exec(`CREATE TABLE "` + tbl + `" (
|
||||
"id" INT IDENTITY(1,1) PRIMARY KEY,
|
||||
"key" VARCHAR(64) NOT NULL,
|
||||
"value" TEXT,
|
||||
"end_time" TIMESTAMP,
|
||||
"state" INT DEFAULT 0,
|
||||
"create_time" TIMESTAMP,
|
||||
"modify_time" TIMESTAMP,
|
||||
CONSTRAINT "uk_` + tbl + `_key" UNIQUE ("key")
|
||||
)`)
|
||||
db.Exec(`CREATE INDEX "idx_` + tbl + `_end_time" ON "` + tbl + `" ("end_time")`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "code.hoteas.com/golang/hotime/db"
|
||||
)
|
||||
|
||||
// SetupMySQLDatabase 为 MySQL 数据库创建测试表并灌入测试数据
|
||||
func SetupMySQLDatabase(db *HoTimeDB) {
|
||||
if db.Type != "mysql" {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("[MySQL Setup] 开始初始化 MySQL 数据库测试环境...")
|
||||
|
||||
prefix := db.GetPrefix()
|
||||
createMySQLTables(db, prefix)
|
||||
seedTestData(db, "MySQL")
|
||||
|
||||
fmt.Println("[MySQL Setup] MySQL 数据库测试环境初始化完成")
|
||||
}
|
||||
|
||||
func mysqlTableExists(db *HoTimeDB, tableName string) bool {
|
||||
rows := db.Query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?", tableName)
|
||||
return len(rows) > 0
|
||||
}
|
||||
|
||||
func createMySQLTables(db *HoTimeDB, prefix string) {
|
||||
// admin 表
|
||||
tbl := prefix + "admin"
|
||||
if !mysqlTableExists(db, tbl) {
|
||||
fmt.Println("[MySQL Setup] 创建表:", tbl)
|
||||
db.Exec("CREATE TABLE `" + tbl + "` (" +
|
||||
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
|
||||
"`name` varchar(100) DEFAULT NULL," +
|
||||
"`phone` varchar(20) DEFAULT NULL," +
|
||||
"`state` int(2) DEFAULT '0'," +
|
||||
"`password` varchar(100) DEFAULT NULL," +
|
||||
"`role_id` int(11) DEFAULT '0'," +
|
||||
"`title` varchar(100) DEFAULT NULL," +
|
||||
"`create_time` datetime DEFAULT NULL," +
|
||||
"`modify_time` datetime DEFAULT NULL," +
|
||||
"PRIMARY KEY (`id`)," +
|
||||
"UNIQUE KEY `uk_phone` (`phone`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
|
||||
}
|
||||
|
||||
// ctg 分类表
|
||||
tbl = prefix + "ctg"
|
||||
if !mysqlTableExists(db, tbl) {
|
||||
fmt.Println("[MySQL Setup] 创建表:", tbl)
|
||||
db.Exec("CREATE TABLE `" + tbl + "` (" +
|
||||
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
|
||||
"`name` varchar(100) DEFAULT NULL," +
|
||||
"`state` int(2) DEFAULT '0'," +
|
||||
"`create_time` datetime DEFAULT NULL," +
|
||||
"`modify_time` datetime DEFAULT NULL," +
|
||||
"PRIMARY KEY (`id`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
|
||||
}
|
||||
|
||||
// article 文章表
|
||||
tbl = prefix + "article"
|
||||
if !mysqlTableExists(db, tbl) {
|
||||
fmt.Println("[MySQL Setup] 创建表:", tbl)
|
||||
db.Exec("CREATE TABLE `" + tbl + "` (" +
|
||||
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
|
||||
"`title` varchar(200) DEFAULT NULL," +
|
||||
"`author` varchar(100) DEFAULT NULL," +
|
||||
"`content` text," +
|
||||
"`state` int(2) DEFAULT '0'," +
|
||||
"`click_num` int(11) DEFAULT '0'," +
|
||||
"`sort` int(11) DEFAULT '0'," +
|
||||
"`img` varchar(500) DEFAULT NULL," +
|
||||
"`ctg_id` int(11) DEFAULT '0'," +
|
||||
"`admin_id` int(11) DEFAULT '0'," +
|
||||
"`create_time` datetime DEFAULT NULL," +
|
||||
"`modify_time` datetime DEFAULT NULL," +
|
||||
"PRIMARY KEY (`id`)," +
|
||||
"KEY `idx_state` (`state`)," +
|
||||
"KEY `idx_ctg_id` (`ctg_id`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
|
||||
}
|
||||
|
||||
// test_batch 测试批量表
|
||||
tbl = prefix + "test_batch"
|
||||
if !mysqlTableExists(db, tbl) {
|
||||
fmt.Println("[MySQL Setup] 创建表:", tbl)
|
||||
db.Exec("CREATE TABLE `" + tbl + "` (" +
|
||||
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
|
||||
"`name` varchar(100) DEFAULT NULL," +
|
||||
"`title` varchar(200) DEFAULT NULL," +
|
||||
"`state` int(2) DEFAULT '0'," +
|
||||
"`create_time` datetime DEFAULT NULL," +
|
||||
"PRIMARY KEY (`id`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
|
||||
}
|
||||
|
||||
// cached 老版本缓存表(兼容模式测试需要)
|
||||
tbl = prefix + "cached"
|
||||
if !mysqlTableExists(db, tbl) {
|
||||
fmt.Println("[MySQL Setup] 创建表:", tbl)
|
||||
db.Exec("CREATE TABLE `" + tbl + "` (" +
|
||||
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
|
||||
"`key` varchar(64) DEFAULT NULL," +
|
||||
"`value` text," +
|
||||
"`endtime` bigint(20) DEFAULT NULL," +
|
||||
"`time` bigint(20) DEFAULT NULL," +
|
||||
"PRIMARY KEY (`id`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
|
||||
}
|
||||
|
||||
// hotime_cache 新版缓存表
|
||||
tbl = prefix + "hotime_cache"
|
||||
if !mysqlTableExists(db, tbl) {
|
||||
fmt.Println("[MySQL Setup] 创建表:", tbl)
|
||||
db.Exec("CREATE TABLE `" + tbl + "` (" +
|
||||
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
|
||||
"`key` varchar(64) NOT NULL," +
|
||||
"`value` text," +
|
||||
"`end_time` datetime DEFAULT NULL," +
|
||||
"`state` int(2) DEFAULT '0'," +
|
||||
"`create_time` datetime DEFAULT NULL," +
|
||||
"`modify_time` datetime DEFAULT NULL," +
|
||||
"PRIMARY KEY (`id`)," +
|
||||
"UNIQUE KEY `uk_key` (`key`)," +
|
||||
"KEY `idx_end_time` (`end_time`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "code.hoteas.com/golang/hotime/db"
|
||||
)
|
||||
|
||||
// SetupDatabase 根据数据库类型自动初始化测试表和种子数据
|
||||
// 在 TestMain 中调用一次即可
|
||||
func SetupDatabase(db *HoTimeDB) {
|
||||
switch db.Type {
|
||||
case "dm", "dameng":
|
||||
SetupDMDatabase(db)
|
||||
case "mysql":
|
||||
SetupMySQLDatabase(db)
|
||||
}
|
||||
}
|
||||
|
||||
// seedTestData 通用种子数据灌入(DM 和 MySQL 共用)
|
||||
// 使用 ORM 的 Insert 方法,与数据库方言无关
|
||||
func seedTestData(db *HoTimeDB, tag string) {
|
||||
// 已有数据则跳过
|
||||
if db.Count("ctg") > 0 {
|
||||
fmt.Printf("[%s Setup] 表中已有数据,跳过数据灌入\n", tag)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("[%s Setup] 灌入测试数据...\n", tag)
|
||||
|
||||
// 分类数据
|
||||
ctgs := []map[string]interface{}{
|
||||
{"name": "新闻资讯", "state": 0},
|
||||
{"name": "技术文档", "state": 0},
|
||||
{"name": "公告通知", "state": 0},
|
||||
{"name": "产品介绍", "state": 0},
|
||||
{"name": "已归档", "state": 1},
|
||||
}
|
||||
for _, c := range ctgs {
|
||||
c["create_time[#]"] = "NOW()"
|
||||
c["modify_time[#]"] = "NOW()"
|
||||
db.Insert("ctg", c)
|
||||
}
|
||||
|
||||
// 管理员数据
|
||||
admins := []map[string]interface{}{
|
||||
{"name": "超级管理员", "phone": "13800000001", "state": 1, "password": "admin123", "role_id": 1, "title": "系统管理员"},
|
||||
{"name": "编辑员", "phone": "13800000002", "state": 1, "password": "editor123", "role_id": 2, "title": "内容编辑"},
|
||||
{"name": "审核员", "phone": "13800000003", "state": 1, "password": "review123", "role_id": 3, "title": "内容审核"},
|
||||
}
|
||||
for _, a := range admins {
|
||||
a["create_time[#]"] = "NOW()"
|
||||
a["modify_time[#]"] = "NOW()"
|
||||
db.Insert("admin", a)
|
||||
}
|
||||
|
||||
// 文章数据 — 覆盖不同状态、分类、点击数、排序
|
||||
articles := []map[string]interface{}{
|
||||
{"title": "数据库入门指南", "author": "管理员", "content": "国产关系型数据库管理系统入门...", "state": 0, "click_num": 128, "sort": 10, "ctg_id": 1, "admin_id": 1},
|
||||
{"title": "新闻发布系统上线公告", "author": "编辑员", "content": "本系统已正式上线运行...", "state": 0, "click_num": 256, "sort": 5, "ctg_id": 1, "admin_id": 2},
|
||||
{"title": "Go语言最佳实践", "author": "管理员", "content": "Go语言在后端开发中的最佳实践总结...", "state": 0, "click_num": 512, "sort": 8, "ctg_id": 2, "admin_id": 1},
|
||||
{"title": "API接口设计规范", "author": "审核员", "content": "RESTful API设计的基本规范和标准...", "state": 0, "click_num": 64, "sort": 15, "ctg_id": 2, "admin_id": 3},
|
||||
{"title": "系统维护通知", "author": "管理员", "content": "系统将于本周日凌晨进行维护升级...", "state": 0, "click_num": 32, "sort": 1, "ctg_id": 3, "admin_id": 1},
|
||||
{"title": "产品功能更新说明", "author": "编辑员", "content": "新版本增加了以下功能...", "state": 0, "click_num": 96, "sort": 12, "ctg_id": 4, "admin_id": 2},
|
||||
{"title": "数据库性能优化指南", "author": "管理员", "content": "数据库性能优化的常见方法和技巧...", "state": 0, "click_num": 1024, "sort": 3, "ctg_id": 2, "admin_id": 1},
|
||||
{"title": "缓存机制深入分析", "author": "审核员", "content": "缓存在Web应用中的重要作用...", "state": 0, "click_num": 200, "sort": 7, "ctg_id": 2, "admin_id": 3},
|
||||
{"title": "旧版公告(已归档)", "author": "管理员", "content": "此公告已过期归档...", "state": 1, "click_num": 10, "sort": 0, "ctg_id": 5, "admin_id": 1},
|
||||
{"title": "测试文章(隐藏)", "author": "编辑员", "content": "测试用文章不公开显示...", "state": 2, "click_num": 0, "sort": 0, "ctg_id": 1, "admin_id": 2},
|
||||
{"title": "高点击量文章", "author": "编辑员", "content": "这是一篇点击量很高的文章...", "state": 0, "click_num": 9999, "sort": 2, "ctg_id": 1, "admin_id": 2},
|
||||
}
|
||||
for _, a := range articles {
|
||||
a["create_time[#]"] = "NOW()"
|
||||
a["modify_time[#]"] = "NOW()"
|
||||
db.Insert("article", a)
|
||||
}
|
||||
|
||||
fmt.Printf("[%s Setup] 数据灌入完成: ctg=%d, admin=%d, article=%d\n",
|
||||
tag, db.Count("ctg"), db.Count("admin"), db.Count("article"))
|
||||
}
|
||||
@@ -0,0 +1,747 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
. "code.hoteas.com/golang/hotime/common"
|
||||
. "code.hoteas.com/golang/hotime/db"
|
||||
)
|
||||
|
||||
// initTestTables 根据数据库类型初始化测试表
|
||||
func initTestTables(that *Context) {
|
||||
switch that.Db.Type {
|
||||
case "dm", "dameng":
|
||||
// 用 COUNT(*) 直接探查表可访问性,而非 USER_TABLES(后者受 schema 错配影响)
|
||||
exists := that.Db.Query(`SELECT COUNT(*) as cnt FROM "test_batch"`)
|
||||
if len(exists) == 0 {
|
||||
that.Db.Exec(`CREATE TABLE "test_batch" (
|
||||
"id" INT IDENTITY(1,1) PRIMARY KEY,
|
||||
"name" VARCHAR(100),
|
||||
"title" VARCHAR(200),
|
||||
"state" INT DEFAULT 0,
|
||||
"create_time" TIMESTAMP
|
||||
)`)
|
||||
}
|
||||
default:
|
||||
that.Db.Exec(`CREATE TABLE IF NOT EXISTS test_batch (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100),
|
||||
title VARCHAR(200),
|
||||
state INT DEFAULT 0,
|
||||
create_time DATETIME
|
||||
)`)
|
||||
}
|
||||
|
||||
_ = that.Db.Count("admin")
|
||||
_ = that.Db.Count("article")
|
||||
}
|
||||
|
||||
// nowFunc 返回当前数据库的 NOW() 函数表达式
|
||||
func nowFunc(that *Context) string {
|
||||
return "NOW()"
|
||||
}
|
||||
|
||||
// TestCtr 通用 ORM 测试控制器(数据库无关)
|
||||
var TestCtr = Ctr{
|
||||
"test": func(that *Context) {
|
||||
that.Display(2, "dsadasd")
|
||||
},
|
||||
|
||||
"all": func(that *Context) {
|
||||
results := Map{}
|
||||
initTestTables(that)
|
||||
|
||||
results["1_basic_crud"] = testBasicCRUD(that)
|
||||
results["2_condition_syntax"] = testConditionSyntax(that)
|
||||
results["3_chain_query"] = testChainQuery(that)
|
||||
results["4_join_query"] = testJoinQuery(that)
|
||||
results["5_aggregate"] = testAggregate(that)
|
||||
results["6_pagination"] = testPagination(that)
|
||||
results["7_batch_insert"] = testInserts(that)
|
||||
results["8_upsert"] = testUpsert(that)
|
||||
results["9_transaction"] = testTransaction(that)
|
||||
|
||||
that.Display(0, results)
|
||||
},
|
||||
|
||||
"crud": func(that *Context) { initTestTables(that); that.Display(0, testBasicCRUD(that)) },
|
||||
"condition": func(that *Context) { that.Display(0, testConditionSyntax(that)) },
|
||||
"chain": func(that *Context) { that.Display(0, testChainQuery(that)) },
|
||||
"join": func(that *Context) { that.Display(0, testJoinQuery(that)) },
|
||||
"aggregate": func(that *Context) { that.Display(0, testAggregate(that)) },
|
||||
"pagination": func(that *Context) { that.Display(0, testPagination(that)) },
|
||||
"batch": func(that *Context) { initTestTables(that); that.Display(0, testInserts(that)) },
|
||||
"upsert": func(that *Context) { that.Display(0, testUpsert(that)) },
|
||||
"transaction": func(that *Context) { initTestTables(that); that.Display(0, testTransaction(that)) },
|
||||
}
|
||||
|
||||
func testBasicCRUD(that *Context) Map {
|
||||
result := Map{"name": "基础CRUD测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
|
||||
now := nowFunc(that)
|
||||
|
||||
insertTest := Map{"name": "Insert 插入测试 (admin表)"}
|
||||
adminId := that.Db.Insert("admin", Map{
|
||||
"name": "测试管理员_" + fmt.Sprintf("%d", time.Now().Unix()),
|
||||
"phone": fmt.Sprintf("138%d", time.Now().Unix()%100000000),
|
||||
"state": 1,
|
||||
"password": "test123456",
|
||||
"role_id": 1,
|
||||
"title": "测试职位",
|
||||
"create_time[#]": now,
|
||||
"modify_time[#]": now,
|
||||
})
|
||||
insertTest["result"] = adminId > 0
|
||||
insertTest["adminId"] = adminId
|
||||
insertTest["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, insertTest)
|
||||
|
||||
getTest := Map{"name": "Get 获取单条记录测试"}
|
||||
admin := that.Db.Get("admin", "*", Map{"id": adminId})
|
||||
getTest["result"] = admin != nil && admin.GetInt64("id") == adminId
|
||||
getTest["admin"] = admin
|
||||
tests = append(tests, getTest)
|
||||
|
||||
selectTest1 := Map{"name": "Select 单条件查询测试"}
|
||||
admins1 := that.Db.Select("admin", "*", Map{"state": 1, "LIMIT": 5})
|
||||
selectTest1["result"] = len(admins1) >= 0
|
||||
selectTest1["count"] = len(admins1)
|
||||
tests = append(tests, selectTest1)
|
||||
|
||||
selectTest2 := Map{"name": "Select 多条件自动AND测试"}
|
||||
admins2 := that.Db.Select("admin", "*", Map{
|
||||
"state": 1,
|
||||
"role_id[>]": 0,
|
||||
"ORDER": "id DESC",
|
||||
"LIMIT": 5,
|
||||
})
|
||||
selectTest2["result"] = true
|
||||
selectTest2["count"] = len(admins2)
|
||||
selectTest2["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, selectTest2)
|
||||
|
||||
updateTest := Map{"name": "Update 更新测试"}
|
||||
affected := that.Db.Update("admin", Map{
|
||||
"title": "更新后的职位",
|
||||
"modify_time[#]": now,
|
||||
}, Map{"id": adminId})
|
||||
updateTest["result"] = affected > 0
|
||||
updateTest["affected"] = affected
|
||||
tests = append(tests, updateTest)
|
||||
|
||||
deleteTest := Map{"name": "Delete 删除测试"}
|
||||
tempId := that.Db.Insert("test_batch", Map{
|
||||
"name": "临时删除测试",
|
||||
"title": "测试标题",
|
||||
"state": 1,
|
||||
"create_time[#]": now,
|
||||
})
|
||||
deleteAffected := that.Db.Delete("test_batch", Map{"id": tempId})
|
||||
deleteTest["result"] = deleteAffected > 0
|
||||
deleteTest["affected"] = deleteAffected
|
||||
tests = append(tests, deleteTest)
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
|
||||
func testConditionSyntax(that *Context) Map {
|
||||
result := Map{"name": "条件查询语法测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
|
||||
test1 := Map{"name": "等于条件 (=)"}
|
||||
articles1 := that.Db.Select("article", "id,title", Map{"state": 0, "LIMIT": 3})
|
||||
test1["result"] = true
|
||||
test1["count"] = len(articles1)
|
||||
tests = append(tests, test1)
|
||||
|
||||
test2 := Map{"name": "不等于条件 ([!])"}
|
||||
articles2 := that.Db.Select("article", "id,title,state", Map{"state[!]": -1, "LIMIT": 3})
|
||||
test2["result"] = true
|
||||
test2["count"] = len(articles2)
|
||||
test2["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test2)
|
||||
|
||||
test3 := Map{"name": "大于小于条件 ([>], [<])"}
|
||||
articles3 := that.Db.Select("article", "id,title,click_num", Map{
|
||||
"click_num[>]": 0,
|
||||
"click_num[<]": 100000,
|
||||
"LIMIT": 3,
|
||||
})
|
||||
test3["result"] = true
|
||||
test3["count"] = len(articles3)
|
||||
tests = append(tests, test3)
|
||||
|
||||
test4 := Map{"name": "大于等于小于等于条件 ([>=], [<=])"}
|
||||
articles4 := that.Db.Select("article", "id,title,sort", Map{
|
||||
"sort[>=]": 0,
|
||||
"sort[<=]": 100,
|
||||
"LIMIT": 3,
|
||||
})
|
||||
test4["result"] = true
|
||||
test4["count"] = len(articles4)
|
||||
tests = append(tests, test4)
|
||||
|
||||
test5 := Map{"name": "LIKE 模糊查询 ([~])"}
|
||||
articles5 := that.Db.Select("article", "id,title", Map{"title[~]": "新闻", "LIMIT": 3})
|
||||
test5["result"] = true
|
||||
test5["count"] = len(articles5)
|
||||
test5["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test5)
|
||||
|
||||
test6 := Map{"name": "右模糊查询 ([~!])"}
|
||||
articles6 := that.Db.Select("admin", "id,name", Map{"name[~!]": "管理", "LIMIT": 3})
|
||||
test6["result"] = true
|
||||
test6["count"] = len(articles6)
|
||||
tests = append(tests, test6)
|
||||
|
||||
test7 := Map{"name": "BETWEEN 区间查询 ([<>])"}
|
||||
articles7 := that.Db.Select("article", "id,title,click_num", Map{"click_num[<>]": Slice{0, 1000}, "LIMIT": 3})
|
||||
test7["result"] = true
|
||||
test7["count"] = len(articles7)
|
||||
test7["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test7)
|
||||
|
||||
test8 := Map{"name": "NOT BETWEEN 查询 ([><])"}
|
||||
articles8 := that.Db.Select("article", "id,title,sort", Map{"sort[><]": Slice{-10, 0}, "LIMIT": 3})
|
||||
test8["result"] = true
|
||||
test8["count"] = len(articles8)
|
||||
tests = append(tests, test8)
|
||||
|
||||
test9 := Map{"name": "IN 查询"}
|
||||
articles9 := that.Db.Select("article", "id,title", Map{"id": Slice{1, 2, 3, 4, 5}, "LIMIT": 5})
|
||||
test9["result"] = true
|
||||
test9["count"] = len(articles9)
|
||||
test9["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test9)
|
||||
|
||||
test10 := Map{"name": "NOT IN 查询 ([!])"}
|
||||
articles10 := that.Db.Select("article", "id,title", Map{"id[!]": Slice{1, 2, 3}, "LIMIT": 5})
|
||||
test10["result"] = true
|
||||
test10["count"] = len(articles10)
|
||||
tests = append(tests, test10)
|
||||
|
||||
test11 := Map{"name": "IS NULL 查询"}
|
||||
articles11 := that.Db.Select("article", "id,title,img", Map{"img": nil, "LIMIT": 3})
|
||||
test11["result"] = true
|
||||
test11["count"] = len(articles11)
|
||||
tests = append(tests, test11)
|
||||
|
||||
test12 := Map{"name": "IS NOT NULL 查询 ([!])"}
|
||||
articles12 := that.Db.Select("article", "id,title,create_time", Map{"create_time[!]": nil, "LIMIT": 3})
|
||||
test12["result"] = true
|
||||
test12["count"] = len(articles12)
|
||||
tests = append(tests, test12)
|
||||
|
||||
// SQL 片段查询根据数据库类型适配
|
||||
test13 := Map{"name": "直接 SQL 片段查询 ([##])"}
|
||||
var sqlFragment string
|
||||
switch that.Db.Type {
|
||||
case "dm", "dameng":
|
||||
sqlFragment = "\"create_time\" > DATEADD(DAY, -365, NOW())"
|
||||
default:
|
||||
sqlFragment = "create_time > DATE_SUB(NOW(), INTERVAL 365 DAY)"
|
||||
}
|
||||
articles13 := that.Db.Select("article", "id,title,create_time", Map{
|
||||
"[##]": sqlFragment,
|
||||
"LIMIT": 3,
|
||||
})
|
||||
test13["result"] = true
|
||||
test13["count"] = len(articles13)
|
||||
test13["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test13)
|
||||
|
||||
test14 := Map{"name": "显式 AND 条件"}
|
||||
articles14 := that.Db.Select("article", "id,title,state,click_num", Map{
|
||||
"AND": Map{
|
||||
"state": 0,
|
||||
"click_num[>=]": 0,
|
||||
},
|
||||
"LIMIT": 3,
|
||||
})
|
||||
test14["result"] = true
|
||||
test14["count"] = len(articles14)
|
||||
tests = append(tests, test14)
|
||||
|
||||
test15 := Map{"name": "OR 条件"}
|
||||
articles15 := that.Db.Select("article", "id,title,sort,click_num", Map{
|
||||
"OR": Map{
|
||||
"sort": 0,
|
||||
"click_num[>]": 10,
|
||||
},
|
||||
"LIMIT": 5,
|
||||
})
|
||||
test15["result"] = true
|
||||
test15["count"] = len(articles15)
|
||||
tests = append(tests, test15)
|
||||
|
||||
test16 := Map{"name": "嵌套 AND/OR 条件"}
|
||||
articles16 := that.Db.Select("article", "id,title,sort,state", Map{
|
||||
"AND": Map{
|
||||
"state": 0,
|
||||
"OR": Map{
|
||||
"sort[>=]": 0,
|
||||
"click_num[>]": 0,
|
||||
},
|
||||
},
|
||||
"LIMIT": 5,
|
||||
})
|
||||
test16["result"] = true
|
||||
test16["count"] = len(articles16)
|
||||
test16["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test16)
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
|
||||
func testChainQuery(that *Context) Map {
|
||||
result := Map{"name": "链式查询测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
|
||||
test1 := Map{"name": "基本链式查询 Table().Where().Select()"}
|
||||
articles1 := that.Db.Table("article").
|
||||
Where("state", 0).
|
||||
Select("id,title,author")
|
||||
test1["result"] = len(articles1) >= 0
|
||||
test1["count"] = len(articles1)
|
||||
tests = append(tests, test1)
|
||||
|
||||
test2 := Map{"name": "链式 And 条件"}
|
||||
articles2 := that.Db.Table("article").
|
||||
Where("state", 0).
|
||||
And("click_num[>=]", 0).
|
||||
And("sort[>=]", 0).
|
||||
Select("id,title,click_num")
|
||||
test2["result"] = len(articles2) >= 0
|
||||
test2["count"] = len(articles2)
|
||||
tests = append(tests, test2)
|
||||
|
||||
test3 := Map{"name": "链式 Or 条件"}
|
||||
articles3 := that.Db.Table("article").
|
||||
Where("state", 0).
|
||||
Or(Map{
|
||||
"sort": 0,
|
||||
"click_num[>]": 10,
|
||||
}).
|
||||
Select("id,title,sort,click_num")
|
||||
test3["result"] = len(articles3) >= 0
|
||||
test3["count"] = len(articles3)
|
||||
tests = append(tests, test3)
|
||||
|
||||
test4 := Map{"name": "链式 Order 排序"}
|
||||
articles4 := that.Db.Table("article").
|
||||
Where("state", 0).
|
||||
Order("create_time DESC", "id ASC").
|
||||
Limit(0, 5).
|
||||
Select("id,title,create_time")
|
||||
test4["result"] = len(articles4) >= 0
|
||||
test4["count"] = len(articles4)
|
||||
tests = append(tests, test4)
|
||||
|
||||
test5 := Map{"name": "链式 Limit 限制"}
|
||||
articles5 := that.Db.Table("article").
|
||||
Where("state", 0).
|
||||
Limit(0, 3).
|
||||
Select("id,title")
|
||||
test5["result"] = len(articles5) <= 3
|
||||
test5["count"] = len(articles5)
|
||||
tests = append(tests, test5)
|
||||
|
||||
test6 := Map{"name": "链式 Get 获取单条"}
|
||||
article6 := that.Db.Table("article").
|
||||
Where("state", 0).
|
||||
Get("id,title,author")
|
||||
test6["result"] = article6 != nil || true
|
||||
test6["article"] = article6
|
||||
tests = append(tests, test6)
|
||||
|
||||
test7 := Map{"name": "链式 Count 统计"}
|
||||
count7 := that.Db.Table("article").
|
||||
Where("state", 0).
|
||||
Count()
|
||||
test7["result"] = count7 >= 0
|
||||
test7["count"] = count7
|
||||
tests = append(tests, test7)
|
||||
|
||||
test8 := Map{"name": "链式 Page 分页"}
|
||||
articles8 := that.Db.Table("article").
|
||||
Where("state", 0).
|
||||
Page(1, 5).
|
||||
Select("id,title")
|
||||
test8["result"] = len(articles8) <= 5
|
||||
test8["count"] = len(articles8)
|
||||
tests = append(tests, test8)
|
||||
|
||||
test9 := Map{"name": "链式 Group 分组"}
|
||||
stats9 := that.Db.Table("article").
|
||||
Where("state", 0).
|
||||
Group("ctg_id").
|
||||
Select("ctg_id, COUNT(*) as cnt")
|
||||
test9["result"] = len(stats9) >= 0
|
||||
test9["stats"] = stats9
|
||||
tests = append(tests, test9)
|
||||
|
||||
test10 := Map{"name": "链式 Update 更新"}
|
||||
testArticle := that.Db.Table("article").Where("state", 0).Get("id")
|
||||
if testArticle != nil {
|
||||
affected := that.Db.Table("article").
|
||||
Where("id", testArticle.GetInt64("id")).
|
||||
Update(Map{"modify_time[#]": nowFunc(that)})
|
||||
test10["result"] = affected >= 0
|
||||
test10["affected"] = affected
|
||||
} else {
|
||||
test10["result"] = true
|
||||
test10["note"] = "无可用测试数据"
|
||||
}
|
||||
tests = append(tests, test10)
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
|
||||
func testJoinQuery(that *Context) Map {
|
||||
result := Map{"name": "JOIN查询测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
|
||||
test1 := Map{"name": "LEFT JOIN 链式查询"}
|
||||
articles1 := that.Db.Table("article").
|
||||
LeftJoin("ctg", "article.ctg_id = ctg.id").
|
||||
Where("article.state", 0).
|
||||
Limit(0, 5).
|
||||
Select("article.id, article.title, ctg.name as ctg_name")
|
||||
test1["result"] = len(articles1) >= 0
|
||||
test1["count"] = len(articles1)
|
||||
test1["data"] = articles1
|
||||
tests = append(tests, test1)
|
||||
|
||||
test2 := Map{"name": "传统 JOIN 语法"}
|
||||
articles2 := that.Db.Select("article",
|
||||
Slice{
|
||||
Map{"[>]ctg": "article.ctg_id = ctg.id"},
|
||||
},
|
||||
"article.id, article.title, ctg.name as ctg_name",
|
||||
Map{
|
||||
"article.state": 0,
|
||||
"LIMIT": 5,
|
||||
})
|
||||
test2["result"] = len(articles2) >= 0
|
||||
test2["count"] = len(articles2)
|
||||
test2["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test2)
|
||||
|
||||
test3 := Map{"name": "多表 JOIN"}
|
||||
articles3 := that.Db.Table("article").
|
||||
LeftJoin("ctg", "article.ctg_id = ctg.id").
|
||||
LeftJoin("admin", "article.admin_id = admin.id").
|
||||
Where("article.state", 0).
|
||||
Limit(0, 5).
|
||||
Select("article.id, article.title, ctg.name as ctg_name, admin.name as admin_name")
|
||||
test3["result"] = len(articles3) >= 0
|
||||
test3["count"] = len(articles3)
|
||||
test3["data"] = articles3
|
||||
tests = append(tests, test3)
|
||||
|
||||
test4 := Map{"name": "INNER JOIN"}
|
||||
articles4 := that.Db.Table("article").
|
||||
InnerJoin("ctg", "article.ctg_id = ctg.id").
|
||||
Where("ctg.state", 0).
|
||||
Limit(0, 5).
|
||||
Select("article.id, article.title, ctg.name as ctg_name")
|
||||
test4["result"] = len(articles4) >= 0
|
||||
test4["count"] = len(articles4)
|
||||
tests = append(tests, test4)
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
|
||||
func testAggregate(that *Context) Map {
|
||||
result := Map{"name": "聚合函数测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
|
||||
test1 := Map{"name": "Count 总数统计"}
|
||||
count1 := that.Db.Count("article")
|
||||
test1["result"] = count1 >= 0
|
||||
test1["count"] = count1
|
||||
tests = append(tests, test1)
|
||||
|
||||
test2 := Map{"name": "Count 条件统计"}
|
||||
count2 := that.Db.Count("article", Map{"state": 0})
|
||||
test2["result"] = count2 >= 0
|
||||
test2["count"] = count2
|
||||
tests = append(tests, test2)
|
||||
|
||||
test3 := Map{"name": "Sum 求和 (单字段名)"}
|
||||
sum3 := that.Db.Sum("article", "click_num", Map{"state": 0})
|
||||
test3["result"] = sum3 >= 0
|
||||
test3["sum"] = sum3
|
||||
test3["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test3)
|
||||
|
||||
test4 := Map{"name": "Avg 平均值 (单字段名)"}
|
||||
avg4 := that.Db.Avg("article", "click_num", Map{"state": 0})
|
||||
test4["result"] = avg4 >= 0
|
||||
test4["avg"] = avg4
|
||||
test4["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test4)
|
||||
|
||||
test5 := Map{"name": "Max 最大值 (单字段名)"}
|
||||
max5 := that.Db.Max("article", "click_num", Map{"state": 0})
|
||||
test5["result"] = max5 >= 0
|
||||
test5["max"] = max5
|
||||
test5["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test5)
|
||||
|
||||
test6 := Map{"name": "Min 最小值 (单字段名)"}
|
||||
min6 := that.Db.Min("article", "sort", Map{"state": 0})
|
||||
test6["result"] = true
|
||||
test6["min"] = min6
|
||||
test6["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test6)
|
||||
|
||||
test7 := Map{"name": "GROUP BY 分组统计"}
|
||||
stats7 := that.Db.Select("article",
|
||||
"ctg_id, COUNT(*) as article_count, AVG(click_num) as avg_clicks, SUM(click_num) as total_clicks",
|
||||
Map{
|
||||
"state": 0,
|
||||
"GROUP": "ctg_id",
|
||||
"ORDER": "article_count DESC",
|
||||
"LIMIT": 10,
|
||||
})
|
||||
test7["result"] = len(stats7) >= 0
|
||||
test7["stats"] = stats7
|
||||
tests = append(tests, test7)
|
||||
|
||||
test8 := Map{"name": "Sum 求和 (table.column 格式)"}
|
||||
sum8 := that.Db.Sum("article", "article.click_num", Map{"state": 0})
|
||||
test8["result"] = sum8 >= 0
|
||||
test8["sum"] = sum8
|
||||
test8["match_single_field"] = sum8 == sum3
|
||||
test8["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test8)
|
||||
|
||||
test9 := Map{"name": "聚合函数一致性验证"}
|
||||
avg9 := that.Db.Avg("article", "article.click_num", Map{"state": 0})
|
||||
max10 := that.Db.Max("article", "article.click_num", Map{"state": 0})
|
||||
min11 := that.Db.Min("article", "article.sort", Map{"state": 0})
|
||||
allMatch := (sum8 == sum3) && (avg9 == avg4) && (max10 == max5) && (min11 == min6)
|
||||
test9["result"] = allMatch
|
||||
test9["summary"] = fmt.Sprintf("Sum: %v=%v, Avg: %v=%v, Max: %v=%v, Min: %v=%v",
|
||||
sum3, sum8, avg4, avg9, max5, max10, min6, min11)
|
||||
tests = append(tests, test9)
|
||||
|
||||
joinSlice := Slice{
|
||||
Map{"[>]ctg": "article.ctg_id = ctg.id"},
|
||||
}
|
||||
test10 := Map{"name": "Sum 带 JOIN (table.column 格式)"}
|
||||
sum10 := that.Db.Sum("article", "article.click_num", joinSlice, Map{"article.state": 0})
|
||||
test10["result"] = sum10 >= 0
|
||||
test10["sum"] = sum10
|
||||
test10["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test10)
|
||||
|
||||
test11 := Map{"name": "Count 带 JOIN"}
|
||||
count11 := that.Db.Count("article", joinSlice, Map{"article.state": 0})
|
||||
test11["result"] = count11 >= 0
|
||||
test11["count"] = count11
|
||||
test11["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test11)
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
|
||||
func testPagination(that *Context) Map {
|
||||
result := Map{"name": "分页查询测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
|
||||
test1 := Map{"name": "PageSelect 分页查询"}
|
||||
articles1 := that.Db.Page(1, 5).PageSelect("article", "*", Map{
|
||||
"state": 0,
|
||||
"ORDER": "id DESC",
|
||||
})
|
||||
test1["result"] = len(articles1) <= 5
|
||||
test1["count"] = len(articles1)
|
||||
tests = append(tests, test1)
|
||||
|
||||
test2 := Map{"name": "PageSelect 第二页"}
|
||||
articles2 := that.Db.Page(2, 5).PageSelect("article", "*", Map{
|
||||
"state": 0,
|
||||
"ORDER": "id DESC",
|
||||
})
|
||||
test2["result"] = len(articles2) <= 5
|
||||
test2["count"] = len(articles2)
|
||||
tests = append(tests, test2)
|
||||
|
||||
test3 := Map{"name": "链式 Page 分页"}
|
||||
articles3 := that.Db.Table("article").
|
||||
Where("state", 0).
|
||||
Order("id DESC").
|
||||
Page(1, 3).
|
||||
Select("id,title,author")
|
||||
test3["result"] = len(articles3) <= 3
|
||||
test3["count"] = len(articles3)
|
||||
tests = append(tests, test3)
|
||||
|
||||
test4 := Map{"name": "Offset 偏移查询"}
|
||||
articles4 := that.Db.Table("article").
|
||||
Where("state", 0).
|
||||
Limit(3).
|
||||
Offset(2).
|
||||
Select("id,title")
|
||||
test4["result"] = len(articles4) <= 3
|
||||
test4["count"] = len(articles4)
|
||||
test4["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test4)
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
|
||||
func testInserts(that *Context) Map {
|
||||
result := Map{"name": "批量插入测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
now := nowFunc(that)
|
||||
|
||||
test1 := Map{"name": "Inserts 批量插入"}
|
||||
timestamp := time.Now().UnixNano()
|
||||
affected1 := that.Db.Inserts("test_batch", []Map{
|
||||
{"name": fmt.Sprintf("批量测试1_%d", timestamp), "title": "标题1", "state": 1},
|
||||
{"name": fmt.Sprintf("批量测试2_%d", timestamp), "title": "标题2", "state": 1},
|
||||
{"name": fmt.Sprintf("批量测试3_%d", timestamp), "title": "标题3", "state": 1},
|
||||
})
|
||||
test1["result"] = affected1 >= 0
|
||||
test1["affected"] = affected1
|
||||
test1["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test1)
|
||||
|
||||
test2 := Map{"name": "Inserts 带 [#] 标记"}
|
||||
timestamp2 := time.Now().UnixNano()
|
||||
affected2 := that.Db.Inserts("test_batch", []Map{
|
||||
{"name": fmt.Sprintf("带时间测试1_%d", timestamp2), "title": "标题带时间1", "state": 1, "create_time[#]": now},
|
||||
{"name": fmt.Sprintf("带时间测试2_%d", timestamp2), "title": "标题带时间2", "state": 1, "create_time[#]": now},
|
||||
})
|
||||
test2["result"] = affected2 >= 0
|
||||
test2["affected"] = affected2
|
||||
tests = append(tests, test2)
|
||||
|
||||
that.Db.Delete("test_batch", Map{"name[~]": fmt.Sprintf("_%d", timestamp)})
|
||||
that.Db.Delete("test_batch", Map{"name[~]": fmt.Sprintf("_%d", timestamp2)})
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
|
||||
func testUpsert(that *Context) Map {
|
||||
result := Map{"name": "Upsert测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
now := nowFunc(that)
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
testPhone := fmt.Sprintf("199%08d", timestamp%100000000)
|
||||
|
||||
test1 := Map{"name": "Upsert 插入新记录 (admin表)"}
|
||||
affected1 := that.Db.Upsert("admin",
|
||||
Map{
|
||||
"name": "Upsert测试管理员",
|
||||
"phone": testPhone,
|
||||
"state": 1,
|
||||
"password": "test123",
|
||||
"role_id": 1,
|
||||
"title": "测试职位",
|
||||
"create_time[#]": now,
|
||||
"modify_time[#]": now,
|
||||
},
|
||||
Slice{"phone"},
|
||||
Slice{"name", "state", "title", "modify_time"},
|
||||
)
|
||||
test1["result"] = affected1 >= 0
|
||||
test1["affected"] = affected1
|
||||
test1["lastQuery"] = that.Db.LastQuery
|
||||
tests = append(tests, test1)
|
||||
|
||||
test2 := Map{"name": "Upsert 更新已存在记录"}
|
||||
affected2 := that.Db.Upsert("admin",
|
||||
Map{
|
||||
"name": "Upsert更新后管理员",
|
||||
"phone": testPhone,
|
||||
"state": 1,
|
||||
"password": "updated123",
|
||||
"role_id": 2,
|
||||
"title": "更新后职位",
|
||||
"create_time[#]": now,
|
||||
"modify_time[#]": now,
|
||||
},
|
||||
Slice{"phone"},
|
||||
Slice{"name", "title", "role_id", "modify_time"},
|
||||
)
|
||||
test2["result"] = affected2 >= 0
|
||||
test2["affected"] = affected2
|
||||
tests = append(tests, test2)
|
||||
|
||||
updatedAdmin := that.Db.Get("admin", "*", Map{"phone": testPhone})
|
||||
test2["updatedAdmin"] = updatedAdmin
|
||||
|
||||
that.Db.Delete("admin", Map{"phone": testPhone})
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
|
||||
func testTransaction(that *Context) Map {
|
||||
result := Map{"name": "事务测试", "tests": Slice{}}
|
||||
tests := Slice{}
|
||||
now := nowFunc(that)
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
testName1 := fmt.Sprintf("事务测试_%d", timestamp)
|
||||
|
||||
test1 := Map{"name": "事务成功提交"}
|
||||
success1 := that.Db.Action(func(tx HoTimeDB) bool {
|
||||
recordId := tx.Insert("test_batch", Map{
|
||||
"name": testName1,
|
||||
"title": "事务提交测试",
|
||||
"state": 1,
|
||||
"create_time[#]": now,
|
||||
})
|
||||
return recordId != 0
|
||||
})
|
||||
test1["result"] = success1
|
||||
checkRecord := that.Db.Get("test_batch", "*", Map{"name": testName1})
|
||||
test1["recordExists"] = checkRecord != nil
|
||||
tests = append(tests, test1)
|
||||
|
||||
test2 := Map{"name": "事务回滚"}
|
||||
testName2 := fmt.Sprintf("事务回滚测试_%d", timestamp)
|
||||
success2 := that.Db.Action(func(tx HoTimeDB) bool {
|
||||
_ = tx.Insert("test_batch", Map{
|
||||
"name": testName2,
|
||||
"title": "事务回滚测试",
|
||||
"state": 1,
|
||||
"create_time[#]": now,
|
||||
})
|
||||
return false
|
||||
})
|
||||
test2["result"] = !success2
|
||||
checkRecord2 := that.Db.Get("test_batch", "*", Map{"name": testName2})
|
||||
test2["recordRolledBack"] = checkRecord2 == nil
|
||||
tests = append(tests, test2)
|
||||
|
||||
that.Db.Delete("test_batch", Map{"name": testName1})
|
||||
|
||||
result["tests"] = tests
|
||||
result["success"] = true
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
)
|
||||
|
||||
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) }},
|
||||
}
|
||||
Reference in New Issue
Block a user