- fun gen [ts|go]:AST 扫描 BindService* 调用,临时生成 go test 辅助采集, 经 fun v1.3.7 公开 API 生成,产物与内置 GenCode 字节级一致(回归测试锁定) - fun run:热更新开发循环,先编译成功再切换,失败保留旧进程 - fun docs:浅色标准文档站——参数表/State 添加字段/请求 JSON 可编辑/ 响应格式化/NDJSON 流式逐行展示,反向代理 /cell 同源无 CORS - 元数据:TS 产物两遍解析重建(枚举/可空/流式工厂/嵌套引用全支持) - 纯标准库实现,零第三方依赖
87 lines
2.1 KiB
Go
87 lines
2.1 KiB
Go
package main
|
|
|
|
// 渲染→解析回环:用渲染器从元数据生成 TS,再解析回元数据,验证解析器覆盖
|
|
// 枚举/可空/流式/嵌套等全部特性
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestTsRenderParseRoundTrip(t *testing.T) {
|
|
meta, err := loadMeta(filepath.Join("testdata", "demo.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
files := fileSet{}
|
|
renderTs{}.renderAll(meta, files)
|
|
dir := t.TempDir()
|
|
if err := writeFiles(dir, files); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
parsed, err := parseTsDir(filepath.Join(dir, "ts"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var ask, create *MetaMethod
|
|
var order, chat *MetaService
|
|
for i := range parsed.Services {
|
|
switch parsed.Services[i].Name {
|
|
case "orderSvc":
|
|
order = &parsed.Services[i]
|
|
case "chatSvc":
|
|
chat = &parsed.Services[i]
|
|
}
|
|
}
|
|
if order == nil || chat == nil {
|
|
t.Fatalf("services missing: %+v", parsed.Services)
|
|
}
|
|
for i := range order.Methods {
|
|
if order.Methods[i].Name == "create" {
|
|
create = &order.Methods[i]
|
|
}
|
|
}
|
|
for i := range chat.Methods {
|
|
if chat.Methods[i].Name == "ask" {
|
|
ask = &chat.Methods[i]
|
|
}
|
|
}
|
|
|
|
if ask == nil || !ask.IsStream || ask.Returns == nil || ask.Returns.Kind != "string" {
|
|
t.Errorf("chatSvc.ask 解析错误: %+v", ask)
|
|
}
|
|
if ask.DTO == nil || len(ask.DTO.Fields) != 1 || ask.DTO.Fields[0].Name != "prompt" {
|
|
t.Errorf("chatSvc.ask DTO 解析错误: %+v", ask.DTO)
|
|
}
|
|
if create == nil || create.Void {
|
|
t.Errorf("orderSvc.create 解析错误: %+v", create)
|
|
}
|
|
if create == nil || create.DTO == nil || len(create.DTO.Fields) != 5 {
|
|
t.Fatalf("createOrderDto 字段数错误: %+v", create.DTO)
|
|
}
|
|
want := map[string]struct {
|
|
kind string
|
|
opt bool
|
|
}{
|
|
"sku": {"string", false},
|
|
"count": {"int", false},
|
|
"note": {"string", true},
|
|
"status": {"enum", true},
|
|
"tags": {"slice", false},
|
|
}
|
|
for _, f := range create.DTO.Fields {
|
|
w, ok := want[f.Name]
|
|
if !ok {
|
|
t.Errorf("多余字段: %s", f.Name)
|
|
continue
|
|
}
|
|
if f.Type.Kind != w.kind || f.Type.Optional != w.opt {
|
|
t.Errorf("字段 %s: kind=%s opt=%v, 期望 %s/%v", f.Name, f.Type.Kind, f.Type.Optional, w.kind, w.opt)
|
|
}
|
|
}
|
|
_ = os.Environ
|
|
}
|