882 lines
22 KiB
Go
882 lines
22 KiB
Go
package system
|
||
|
||
import (
|
||
"bytes"
|
||
"go/format"
|
||
"strings"
|
||
"testing"
|
||
"text/template"
|
||
|
||
"kra/pkg/utils/autocode"
|
||
)
|
||
|
||
// TestTemplateFuncMap 测试模板函数映射是否正确注册
|
||
func TestTemplateFuncMap(t *testing.T) {
|
||
funcMap := autocode.GetTemplateFuncMap()
|
||
|
||
// 验证必要的函数已注册
|
||
requiredFuncs := []string{
|
||
"title",
|
||
"toPascalCase",
|
||
"GenerateField",
|
||
"GenerateModelField",
|
||
"GenerateSearchField",
|
||
"GenerateSearchConditions",
|
||
"GenerateGormGenSearchConditions",
|
||
"GenerateSearchFormItem",
|
||
"GenerateTableColumn",
|
||
"GenerateFormItem",
|
||
"GenerateDescriptionItem",
|
||
"GenerateDefaultFormValue",
|
||
"GenerateTSType",
|
||
"GenerateReactSearchFormItem",
|
||
"GenerateReactTableColumn",
|
||
"GenerateReactProTableColumn",
|
||
"GenerateReactFormItem",
|
||
"GenerateReactDescriptionItem",
|
||
"GenerateReactDefaultValue",
|
||
"GenerateCustomMethodInterface",
|
||
"GenerateCustomMethodImpl",
|
||
}
|
||
|
||
for _, funcName := range requiredFuncs {
|
||
if _, ok := funcMap[funcName]; !ok {
|
||
t.Errorf("模板函数 %s 未注册", funcName)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestGenerateTSType 测试 Go 类型到 TypeScript 类型的映射
|
||
func TestGenerateTSType(t *testing.T) {
|
||
testCases := []struct {
|
||
goType string
|
||
expected string
|
||
}{
|
||
// 整数类型
|
||
{"int", "number"},
|
||
{"int8", "number"},
|
||
{"int16", "number"},
|
||
{"int32", "number"},
|
||
{"int64", "number"},
|
||
// 无符号整数类型
|
||
{"uint", "number"},
|
||
{"uint8", "number"},
|
||
{"uint16", "number"},
|
||
{"uint32", "number"},
|
||
{"uint64", "number"},
|
||
// 浮点类型
|
||
{"float32", "number"},
|
||
{"float64", "number"},
|
||
// 基础类型
|
||
{"string", "string"},
|
||
{"bool", "boolean"},
|
||
// 时间类型
|
||
{"time.Time", "string"},
|
||
// JSON 类型
|
||
{"datatypes.JSON", "Record<string, any>"},
|
||
{"json", "Record<string, any>"},
|
||
// 媒体类型
|
||
{"picture", "string"},
|
||
{"pictures", "string[]"},
|
||
{"video", "string"},
|
||
{"file", "Array<{ name: string; url: string; uid: string }>"},
|
||
// 其他类型
|
||
{"array", "any[]"},
|
||
{"richtext", "string"},
|
||
{"enum", "string"},
|
||
// 指针类型
|
||
{"*string", "string"},
|
||
{"*int", "number"},
|
||
{"*bool", "boolean"},
|
||
{"*time.Time", "string"},
|
||
{"*int64", "number"},
|
||
{"*float64", "number"},
|
||
// 未知类型
|
||
{"unknown", "any"},
|
||
{"customType", "any"},
|
||
{"", "any"},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.goType, func(t *testing.T) {
|
||
result := autocode.GenerateTSType(tc.goType)
|
||
if result != tc.expected {
|
||
t.Errorf("GenerateTSType(%s) = %s, 期望 %s", tc.goType, result, tc.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestGenerateTSTypePointerRecursion 测试指针类型的递归处理
|
||
func TestGenerateTSTypePointerRecursion(t *testing.T) {
|
||
testCases := []struct {
|
||
name string
|
||
goType string
|
||
expected string
|
||
}{
|
||
{"单层指针-string", "*string", "string"},
|
||
{"单层指针-int", "*int", "number"},
|
||
{"单层指针-bool", "*bool", "boolean"},
|
||
{"单层指针-time", "*time.Time", "string"},
|
||
{"单层指针-json", "*datatypes.JSON", "Record<string, any>"},
|
||
{"单层指针-未知类型", "*unknownType", "any"},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
result := autocode.GenerateTSType(tc.goType)
|
||
if result != tc.expected {
|
||
t.Errorf("GenerateTSType(%s) = %s, 期望 %s", tc.goType, result, tc.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestGenerateTSTypeAllNumericTypes 测试所有数值类型映射
|
||
func TestGenerateTSTypeAllNumericTypes(t *testing.T) {
|
||
numericTypes := []string{
|
||
"int", "int8", "int16", "int32", "int64",
|
||
"uint", "uint8", "uint16", "uint32", "uint64",
|
||
"float32", "float64",
|
||
}
|
||
|
||
for _, goType := range numericTypes {
|
||
t.Run(goType, func(t *testing.T) {
|
||
result := autocode.GenerateTSType(goType)
|
||
if result != "number" {
|
||
t.Errorf("GenerateTSType(%s) = %s, 期望 number", goType, result)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestGenerateTSTypeMediaTypes 测试媒体类型映射
|
||
func TestGenerateTSTypeMediaTypes(t *testing.T) {
|
||
testCases := []struct {
|
||
goType string
|
||
expected string
|
||
}{
|
||
{"picture", "string"},
|
||
{"pictures", "string[]"},
|
||
{"video", "string"},
|
||
{"file", "Array<{ name: string; url: string; uid: string }>"},
|
||
{"richtext", "string"},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.goType, func(t *testing.T) {
|
||
result := autocode.GenerateTSType(tc.goType)
|
||
if result != tc.expected {
|
||
t.Errorf("GenerateTSType(%s) = %s, 期望 %s", tc.goType, result, tc.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestToPascalCase 测试 PascalCase 转换
|
||
func TestToPascalCase(t *testing.T) {
|
||
testCases := []struct {
|
||
input string
|
||
expected string
|
||
}{
|
||
{"article_status", "ArticleStatus"},
|
||
{"user-role", "UserRole"},
|
||
{"hello world", "HelloWorld"},
|
||
{"simple", "Simple"},
|
||
{"", ""},
|
||
{"already_Pascal_Case", "AlreadyPascalCase"},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
result := autocode.ToPascalCase(tc.input)
|
||
if result != tc.expected {
|
||
t.Errorf("ToPascalCase(%s) = %s, 期望 %s", tc.input, result, tc.expected)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestGenerateModelField 测试 GORM Gen 模型字段生成
|
||
func TestGenerateModelField(t *testing.T) {
|
||
testCases := []struct {
|
||
name string
|
||
field autocode.AutoCodeField
|
||
contains []string // 生成的字段应包含的内容
|
||
}{
|
||
{
|
||
name: "字符串字段",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Title",
|
||
FieldDesc: "标题",
|
||
FieldType: "string",
|
||
FieldJson: "title",
|
||
ColumnName: "title",
|
||
DataTypeLong: "255",
|
||
},
|
||
contains: []string{"Title", "string", "gorm:", "column:title", "json:\"title\"", "标题"},
|
||
},
|
||
{
|
||
name: "整数字段",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Status",
|
||
FieldDesc: "状态",
|
||
FieldType: "int",
|
||
FieldJson: "status",
|
||
ColumnName: "status",
|
||
DataTypeLong: "1",
|
||
},
|
||
contains: []string{"Status", "int8", "gorm:", "column:status", "json:\"status\""},
|
||
},
|
||
{
|
||
name: "时间字段",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "CreatedAt",
|
||
FieldDesc: "创建时间",
|
||
FieldType: "time.Time",
|
||
FieldJson: "createdAt",
|
||
ColumnName: "created_at",
|
||
},
|
||
contains: []string{"CreatedAt", "time.Time", "gorm:", "column:created_at"},
|
||
},
|
||
{
|
||
name: "布尔字段",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "IsActive",
|
||
FieldDesc: "是否激活",
|
||
FieldType: "bool",
|
||
FieldJson: "isActive",
|
||
ColumnName: "is_active",
|
||
},
|
||
contains: []string{"IsActive", "bool", "gorm:", "column:is_active"},
|
||
},
|
||
{
|
||
name: "JSON字段",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Extra",
|
||
FieldDesc: "扩展信息",
|
||
FieldType: "json",
|
||
FieldJson: "extra",
|
||
ColumnName: "extra",
|
||
},
|
||
contains: []string{"Extra", "datatypes.JSON", "gorm:", "column:extra"},
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
result := autocode.GenerateModelField(tc.field)
|
||
for _, expected := range tc.contains {
|
||
if !strings.Contains(result, expected) {
|
||
t.Errorf("GenerateModelField 结果应包含 %s, 实际结果: %s", expected, result)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestGenerateGormGenSearchConditions 测试 GORM Gen 搜索条件生成
|
||
func TestGenerateGormGenSearchConditions(t *testing.T) {
|
||
fields := []*autocode.AutoCodeField{
|
||
{
|
||
FieldName: "Title",
|
||
FieldType: "string",
|
||
FieldSearchType: "LIKE",
|
||
},
|
||
{
|
||
FieldName: "Status",
|
||
FieldType: "int",
|
||
FieldSearchType: "EQ",
|
||
},
|
||
{
|
||
FieldName: "CreatedAt",
|
||
FieldType: "time.Time",
|
||
FieldSearchType: "BETWEEN",
|
||
},
|
||
}
|
||
|
||
result := autocode.GenerateGormGenSearchConditions(fields, "Article")
|
||
|
||
// 验证 LIKE 条件
|
||
if !strings.Contains(result, "t.Title.Like") {
|
||
t.Error("LIKE 搜索条件应使用 t.Title.Like")
|
||
}
|
||
|
||
// 验证 EQ 条件
|
||
if !strings.Contains(result, "t.Status.Eq") {
|
||
t.Error("EQ 搜索条件应使用 t.Status.Eq")
|
||
}
|
||
|
||
// 验证 BETWEEN 条件
|
||
if !strings.Contains(result, "t.CreatedAt.Between") {
|
||
t.Error("BETWEEN 搜索条件应使用 t.CreatedAt.Between")
|
||
}
|
||
}
|
||
|
||
// TestGenerateReactFormItem 测试 React 表单项生成
|
||
func TestGenerateReactFormItem(t *testing.T) {
|
||
testCases := []struct {
|
||
name string
|
||
field autocode.AutoCodeField
|
||
contains []string
|
||
}{
|
||
{
|
||
name: "字符串输入框",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Title",
|
||
FieldDesc: "标题",
|
||
FieldType: "string",
|
||
FieldJson: "title",
|
||
Require: true,
|
||
},
|
||
contains: []string{"Form.Item", "name=\"title\"", "label=\"标题\"", "Input", "required: true"},
|
||
},
|
||
{
|
||
name: "布尔开关",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "IsActive",
|
||
FieldDesc: "是否激活",
|
||
FieldType: "bool",
|
||
FieldJson: "isActive",
|
||
},
|
||
contains: []string{"Form.Item", "name=\"isActive\"", "Switch", "valuePropName=\"checked\""},
|
||
},
|
||
{
|
||
name: "数字输入框",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Count",
|
||
FieldDesc: "数量",
|
||
FieldType: "int",
|
||
FieldJson: "count",
|
||
},
|
||
contains: []string{"Form.Item", "name=\"count\"", "InputNumber"},
|
||
},
|
||
{
|
||
name: "日期选择器",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "PublishDate",
|
||
FieldDesc: "发布日期",
|
||
FieldType: "time.Time",
|
||
FieldJson: "publishDate",
|
||
},
|
||
contains: []string{"Form.Item", "name=\"publishDate\"", "DatePicker", "showTime"},
|
||
},
|
||
{
|
||
name: "字典选择器",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Status",
|
||
FieldDesc: "状态",
|
||
FieldType: "string",
|
||
FieldJson: "status",
|
||
DictType: "article_status",
|
||
},
|
||
contains: []string{"Form.Item", "name=\"status\"", "Select", "article_statusOptions"},
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
result := autocode.GenerateReactFormItem(tc.field)
|
||
for _, expected := range tc.contains {
|
||
if !strings.Contains(result, expected) {
|
||
t.Errorf("GenerateReactFormItem 结果应包含 %s, 实际结果: %s", expected, result)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestGenerateReactTableColumn 测试 React 表格列生成
|
||
func TestGenerateReactTableColumn(t *testing.T) {
|
||
testCases := []struct {
|
||
name string
|
||
field autocode.AutoCodeField
|
||
contains []string
|
||
}{
|
||
{
|
||
name: "普通字符串列",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Title",
|
||
FieldDesc: "标题",
|
||
FieldType: "string",
|
||
FieldJson: "title",
|
||
},
|
||
contains: []string{"title: '标题'", "dataIndex: 'title'", "key: 'title'"},
|
||
},
|
||
{
|
||
name: "可排序列",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "CreatedAt",
|
||
FieldDesc: "创建时间",
|
||
FieldType: "time.Time",
|
||
FieldJson: "createdAt",
|
||
Sort: true,
|
||
},
|
||
contains: []string{"title: '创建时间'", "sorter: true", "dayjs"},
|
||
},
|
||
{
|
||
name: "布尔列",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "IsActive",
|
||
FieldDesc: "是否激活",
|
||
FieldType: "bool",
|
||
FieldJson: "isActive",
|
||
},
|
||
contains: []string{"title: '是否激活'", "是", "否"},
|
||
},
|
||
{
|
||
name: "图片列",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Cover",
|
||
FieldDesc: "封面",
|
||
FieldType: "picture",
|
||
FieldJson: "cover",
|
||
},
|
||
contains: []string{"title: '封面'", "Image"},
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
result := autocode.GenerateReactTableColumn(tc.field)
|
||
for _, expected := range tc.contains {
|
||
if !strings.Contains(result, expected) {
|
||
t.Errorf("GenerateReactTableColumn 结果应包含 %s, 实际结果: %s", expected, result)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestGenerateReactDefaultValue 测试 React 默认值生成
|
||
func TestGenerateReactDefaultValue(t *testing.T) {
|
||
testCases := []struct {
|
||
fieldType string
|
||
fieldJson string
|
||
expected string
|
||
}{
|
||
{"bool", "isActive", "isActive: false,"},
|
||
{"string", "title", "title: '',"},
|
||
{"int", "count", "count: 0,"},
|
||
{"float64", "price", "price: 0,"},
|
||
{"time.Time", "createdAt", "createdAt: undefined,"},
|
||
{"pictures", "images", "images: [],"},
|
||
{"file", "attachment", "attachment: [],"},
|
||
{"json", "extra", "extra: {},"},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
field := autocode.AutoCodeField{
|
||
FieldType: tc.fieldType,
|
||
FieldJson: tc.fieldJson,
|
||
}
|
||
result := autocode.GenerateReactDefaultValue(field)
|
||
if result != tc.expected {
|
||
t.Errorf("GenerateReactDefaultValue(%s) = %s, 期望 %s", tc.fieldType, result, tc.expected)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestTemplateRenderingBasic 测试基本模板渲染功能
|
||
func TestTemplateRenderingBasic(t *testing.T) {
|
||
// 简单模板测试
|
||
tplContent := `package {{.Package}}
|
||
|
||
type {{.StructName}} struct {
|
||
ID int64
|
||
}
|
||
`
|
||
data := map[string]interface{}{
|
||
"Package": "example",
|
||
"StructName": "Article",
|
||
}
|
||
|
||
tmpl, err := template.New("test").Funcs(autocode.GetTemplateFuncMap()).Parse(tplContent)
|
||
if err != nil {
|
||
t.Fatalf("模板解析失败: %v", err)
|
||
}
|
||
|
||
var buf bytes.Buffer
|
||
if err := tmpl.Execute(&buf, data); err != nil {
|
||
t.Fatalf("模板执行失败: %v", err)
|
||
}
|
||
|
||
result := buf.String()
|
||
|
||
// 验证渲染结果
|
||
if !strings.Contains(result, "package example") {
|
||
t.Error("渲染结果应包含 'package example'")
|
||
}
|
||
if !strings.Contains(result, "type Article struct") {
|
||
t.Error("渲染结果应包含 'type Article struct'")
|
||
}
|
||
}
|
||
|
||
// TestTemplateRenderingWithFunctions 测试带模板函数的渲染
|
||
func TestTemplateRenderingWithFunctions(t *testing.T) {
|
||
tplContent := `// {{.Description}}类型定义
|
||
export interface {{.StructName}} {
|
||
{{- range .Fields}}
|
||
{{.FieldJson}}: {{ GenerateTSType .FieldType }}; // {{.FieldDesc}}
|
||
{{- end}}
|
||
}
|
||
`
|
||
data := map[string]interface{}{
|
||
"Description": "文章",
|
||
"StructName": "Article",
|
||
"Fields": []autocode.AutoCodeField{
|
||
{FieldJson: "title", FieldType: "string", FieldDesc: "标题"},
|
||
{FieldJson: "content", FieldType: "string", FieldDesc: "内容"},
|
||
{FieldJson: "viewCount", FieldType: "int", FieldDesc: "浏览量"},
|
||
{FieldJson: "isPublished", FieldType: "bool", FieldDesc: "是否发布"},
|
||
},
|
||
}
|
||
|
||
tmpl, err := template.New("test").Funcs(autocode.GetTemplateFuncMap()).Parse(tplContent)
|
||
if err != nil {
|
||
t.Fatalf("模板解析失败: %v", err)
|
||
}
|
||
|
||
var buf bytes.Buffer
|
||
if err := tmpl.Execute(&buf, data); err != nil {
|
||
t.Fatalf("模板执行失败: %v", err)
|
||
}
|
||
|
||
result := buf.String()
|
||
|
||
// 验证 TypeScript 类型映射
|
||
if !strings.Contains(result, "title: string;") {
|
||
t.Error("渲染结果应包含 'title: string;'")
|
||
}
|
||
if !strings.Contains(result, "viewCount: number;") {
|
||
t.Error("渲染结果应包含 'viewCount: number;'")
|
||
}
|
||
if !strings.Contains(result, "isPublished: boolean;") {
|
||
t.Error("渲染结果应包含 'isPublished: boolean;'")
|
||
}
|
||
}
|
||
|
||
// TestGoCodeFormatting 测试 Go 代码格式化(Property 4: 模板渲染幂等性)
|
||
func TestGoCodeFormatting(t *testing.T) {
|
||
// 生成的 Go 代码
|
||
goCode := `package example
|
||
|
||
import "fmt"
|
||
|
||
type Article struct {
|
||
ID int64
|
||
Title string
|
||
Content string
|
||
}
|
||
|
||
func (a *Article) String() string {
|
||
return fmt.Sprintf("Article{ID: %d, Title: %s}", a.ID, a.Title)
|
||
}
|
||
`
|
||
// 格式化代码
|
||
formatted, err := format.Source([]byte(goCode))
|
||
if err != nil {
|
||
t.Fatalf("代码格式化失败: %v", err)
|
||
}
|
||
|
||
// 再次格式化,验证幂等性
|
||
formattedAgain, err := format.Source(formatted)
|
||
if err != nil {
|
||
t.Fatalf("二次格式化失败: %v", err)
|
||
}
|
||
|
||
// 验证两次格式化结果相同
|
||
if string(formatted) != string(formattedAgain) {
|
||
t.Error("格式化应该是幂等的,两次格式化结果应相同")
|
||
}
|
||
}
|
||
|
||
// TestGenerateSearchField 测试搜索字段生成
|
||
func TestGenerateSearchField(t *testing.T) {
|
||
testCases := []struct {
|
||
name string
|
||
field autocode.AutoCodeField
|
||
contains []string
|
||
isEmpty bool
|
||
}{
|
||
{
|
||
name: "无搜索类型",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Title",
|
||
FieldType: "string",
|
||
FieldJson: "title",
|
||
FieldSearchType: "",
|
||
},
|
||
isEmpty: true,
|
||
},
|
||
{
|
||
name: "LIKE搜索",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Title",
|
||
FieldType: "string",
|
||
FieldJson: "title",
|
||
FieldSearchType: "LIKE",
|
||
},
|
||
contains: []string{"Title", "*string", "json:\"title\""},
|
||
},
|
||
{
|
||
name: "BETWEEN搜索-时间",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "CreatedAt",
|
||
FieldType: "time.Time",
|
||
FieldJson: "createdAt",
|
||
FieldSearchType: "BETWEEN",
|
||
},
|
||
contains: []string{"CreatedAtRange", "[]time.Time"},
|
||
},
|
||
{
|
||
name: "BETWEEN搜索-数字",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Price",
|
||
FieldType: "float64",
|
||
FieldJson: "price",
|
||
FieldSearchType: "BETWEEN",
|
||
},
|
||
contains: []string{"StartPrice", "EndPrice", "*float64"},
|
||
},
|
||
{
|
||
name: "IN搜索",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Status",
|
||
FieldType: "int",
|
||
FieldJson: "status",
|
||
FieldSearchType: "IN",
|
||
},
|
||
contains: []string{"Status", "[]int"},
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
result := autocode.GenerateSearchField(tc.field)
|
||
if tc.isEmpty {
|
||
if result != "" {
|
||
t.Errorf("期望空字符串,实际: %s", result)
|
||
}
|
||
return
|
||
}
|
||
for _, expected := range tc.contains {
|
||
if !strings.Contains(result, expected) {
|
||
t.Errorf("GenerateSearchField 结果应包含 %s, 实际结果: %s", expected, result)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestGenerateReactDescriptionItem 测试 React 描述项生成
|
||
func TestGenerateReactDescriptionItem(t *testing.T) {
|
||
testCases := []struct {
|
||
name string
|
||
field autocode.AutoCodeField
|
||
contains []string
|
||
}{
|
||
{
|
||
name: "普通字符串",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Title",
|
||
FieldDesc: "标题",
|
||
FieldType: "string",
|
||
FieldJson: "title",
|
||
},
|
||
contains: []string{"Descriptions.Item", "label=\"标题\"", "currentRecord?.title"},
|
||
},
|
||
{
|
||
name: "布尔类型",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "IsActive",
|
||
FieldDesc: "是否激活",
|
||
FieldType: "bool",
|
||
FieldJson: "isActive",
|
||
},
|
||
contains: []string{"Descriptions.Item", "是", "否"},
|
||
},
|
||
{
|
||
name: "时间类型",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "CreatedAt",
|
||
FieldDesc: "创建时间",
|
||
FieldType: "time.Time",
|
||
FieldJson: "createdAt",
|
||
},
|
||
contains: []string{"Descriptions.Item", "dayjs", "format"},
|
||
},
|
||
{
|
||
name: "图片类型",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Cover",
|
||
FieldDesc: "封面",
|
||
FieldType: "picture",
|
||
FieldJson: "cover",
|
||
},
|
||
contains: []string{"Descriptions.Item", "Image"},
|
||
},
|
||
{
|
||
name: "JSON类型",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Extra",
|
||
FieldDesc: "扩展信息",
|
||
FieldType: "json",
|
||
FieldJson: "extra",
|
||
},
|
||
contains: []string{"Descriptions.Item", "JSON.stringify"},
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
result := autocode.GenerateReactDescriptionItem(tc.field)
|
||
for _, expected := range tc.contains {
|
||
if !strings.Contains(result, expected) {
|
||
t.Errorf("GenerateReactDescriptionItem 结果应包含 %s, 实际结果: %s", expected, result)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestGenerateReactProTableColumn 测试 Ant Design Pro Table 列配置生成
|
||
func TestGenerateReactProTableColumn(t *testing.T) {
|
||
testCases := []struct {
|
||
name string
|
||
field autocode.AutoCodeField
|
||
contains []string
|
||
}{
|
||
{
|
||
name: "普通文本列",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Title",
|
||
FieldDesc: "标题",
|
||
FieldType: "string",
|
||
FieldJson: "title",
|
||
},
|
||
contains: []string{"title: '标题'", "dataIndex: 'title'", "ellipsis: true"},
|
||
},
|
||
{
|
||
name: "布尔列",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "IsActive",
|
||
FieldDesc: "是否激活",
|
||
FieldType: "bool",
|
||
FieldJson: "isActive",
|
||
},
|
||
contains: []string{"valueType: 'select'", "valueEnum", "是", "否"},
|
||
},
|
||
{
|
||
name: "时间列-范围搜索",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "CreatedAt",
|
||
FieldDesc: "创建时间",
|
||
FieldType: "time.Time",
|
||
FieldJson: "createdAt",
|
||
FieldSearchType: "BETWEEN",
|
||
},
|
||
contains: []string{"valueType: 'dateTimeRange'"},
|
||
},
|
||
{
|
||
name: "数字列",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Count",
|
||
FieldDesc: "数量",
|
||
FieldType: "int",
|
||
FieldJson: "count",
|
||
FieldSearchType: "EQ",
|
||
},
|
||
contains: []string{"valueType: 'digit'"},
|
||
},
|
||
{
|
||
name: "字典列",
|
||
field: autocode.AutoCodeField{
|
||
FieldName: "Status",
|
||
FieldDesc: "状态",
|
||
FieldType: "string",
|
||
FieldJson: "status",
|
||
DictType: "article_status",
|
||
},
|
||
contains: []string{"valueType: 'select'", "article_statusOptions"},
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
result := autocode.GenerateReactProTableColumn(tc.field)
|
||
for _, expected := range tc.contains {
|
||
if !strings.Contains(result, expected) {
|
||
t.Errorf("GenerateReactProTableColumn 结果应包含 %s, 实际结果: %s", expected, result)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestComplexTemplateRendering 测试复杂模板渲染(模拟实际 biz 层模板)
|
||
func TestComplexTemplateRendering(t *testing.T) {
|
||
tplContent := `package {{.Package}}
|
||
|
||
import (
|
||
"context"
|
||
"{{.Module}}/internal/data/model/{{.Package}}"
|
||
"github.com/go-kratos/kratos/v2/log"
|
||
)
|
||
|
||
// {{.StructName}}Repo {{.Description}}仓储接口
|
||
type {{.StructName}}Repo interface {
|
||
Create(ctx context.Context, {{.Abbreviation}} *{{.Package}}.{{.StructName}}) error
|
||
Delete(ctx context.Context, {{.PrimaryField.FieldJson}} {{.PrimaryField.FieldType}}) error
|
||
Update(ctx context.Context, {{.Abbreviation}} *{{.Package}}.{{.StructName}}) error
|
||
FindByID(ctx context.Context, {{.PrimaryField.FieldJson}} {{.PrimaryField.FieldType}}) (*{{.Package}}.{{.StructName}}, error)
|
||
}
|
||
|
||
// {{.StructName}}Usecase {{.Description}}用例
|
||
type {{.StructName}}Usecase struct {
|
||
repo {{.StructName}}Repo
|
||
log *log.Helper
|
||
}
|
||
`
|
||
data := map[string]interface{}{
|
||
"Package": "article",
|
||
"Module": "kra",
|
||
"StructName": "Article",
|
||
"Description": "文章",
|
||
"Abbreviation": "article",
|
||
"PrimaryField": map[string]interface{}{
|
||
"FieldJson": "id",
|
||
"FieldType": "int64",
|
||
},
|
||
}
|
||
|
||
tmpl, err := template.New("test").Funcs(autocode.GetTemplateFuncMap()).Parse(tplContent)
|
||
if err != nil {
|
||
t.Fatalf("模板解析失败: %v", err)
|
||
}
|
||
|
||
var buf bytes.Buffer
|
||
if err := tmpl.Execute(&buf, data); err != nil {
|
||
t.Fatalf("模板执行失败: %v", err)
|
||
}
|
||
|
||
result := buf.String()
|
||
|
||
// 验证关键内容
|
||
expectedContents := []string{
|
||
"package article",
|
||
"ArticleRepo interface",
|
||
"ArticleUsecase struct",
|
||
"Create(ctx context.Context",
|
||
"Delete(ctx context.Context",
|
||
"Update(ctx context.Context",
|
||
"FindByID(ctx context.Context",
|
||
"*article.Article",
|
||
"log.Helper",
|
||
}
|
||
|
||
for _, expected := range expectedContents {
|
||
if !strings.Contains(result, expected) {
|
||
t.Errorf("渲染结果应包含 '%s'", expected)
|
||
}
|
||
}
|
||
}
|