Files
opsxapi/server/internal/mocksrv/mocksrv_test.go
T
logan 2bb4ef7a95 桌面 GUI 外链修复 + 主题顺序调整(含结构扁平化进行中的工作区基线)
- 集成 tauri-plugin-opener:Cargo/lib.rs/capabilities/npm 四处接线,新增 src/openExternal.ts(桌面走 opener,浏览器回退 window.open,相对路径绝对化)
- 关于页 4 个链接、活动栏官网菜单、代理错误横幅 2 处健康检查链接全部改走 openExternal,桌面端可正常跳转
- 设置→外观 主题顺序调整为 深色/浅色/跟随系统
- release 二进制重编并验证(浏览器实测关于页链接可开文档页)
- 附带工作区中未提交的扁平化重构现状(另一会话进行中)
- homepage 落地页与文档终检通过(8787 / 与 /docs 双 200)
2026-10-16 03:03:36 +08:00

151 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package mocksrv
import (
"encoding/json"
"regexp"
"strings"
"testing"
"time"
)
func TestGenerateFormats(t *testing.T) {
cases := []struct {
expr string
re string
}{
{"@integer(3, 7), extra", ""},
}
_ = cases
checks := map[string]*regexp.Regexp{
"@integer(3, 7)": regexp.MustCompile(`^[3-7]$`),
"@float(1, 9, 2)": regexp.MustCompile(`^\d\.\d{2}$`),
"@email": regexp.MustCompile(`^[\w.]+@[\w.]+\.\w+$`),
"@phone": regexp.MustCompile(`^1[3-9]\d{9}$`),
"@guid": regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`),
"@id": regexp.MustCompile(`^\d{17}[\dX]$`),
"@datetime(YYYY-MM-DD HH:mm:ss)": regexp.MustCompile(`^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`),
"@timestamp": regexp.MustCompile(`^\d{12,}$`),
"@boolean": regexp.MustCompile(`^(true|false)$`),
"@cname": regexp.MustCompile(`^\p{Han}{2,3}$`),
"@county(true)": regexp.MustCompile(`\p{Han}+(市)?\p{Han}+区$`),
"@pick(\"男\", \"女\")": regexp.MustCompile(`^(男|女)$`),
"@notExistThing": regexp.MustCompile(`^@notExistThing$`), // 未知表达式原样
"普通文本": regexp.MustCompile(`^普通文本$`),
}
for expr, re := range checks {
for i := 0; i < 30; i++ {
out := Generate(expr)
if !re.MatchString(out) {
t.Fatalf("%s → %q 不匹配 %v", expr, out, re)
}
}
}
}
func TestGenerateJSONLeaves(t *testing.T) {
tpl := `{"username":"@cname","age":"@integer(18,65)","vip":"@boolean","keep":"固定值"}`
var v map[string]interface{}
if err := json.Unmarshal([]byte(GenerateJSON(tpl)), &v); err != nil {
t.Fatalf("生成结果非 JSON: %v", err)
}
age, ok := v["age"].(float64)
if !ok || age < 18 || age > 65 {
t.Fatalf("age 应为 18-65 的数字,得到 %v", v["age"])
}
if _, ok := v["vip"].(bool); !ok {
t.Fatalf("vip 应为布尔,得到 %T", v["vip"])
}
if v["keep"] != "固定值" {
t.Fatalf("非表达式叶子应原样保留")
}
if s, _ := v["username"].(string); len(s) < 2 {
t.Fatalf("username 生成异常: %v", v["username"])
}
}
func TestGenerateJSONTopLevelAndInvalid(t *testing.T) {
if out := GenerateJSON("@cname"); len([]rune(out)) < 2 {
t.Fatalf("顶层表达式应生成: %q", out)
}
if out := GenerateJSON("not-json"); out != "not-json" {
t.Fatalf("非法 JSON 原样返回: %q", out)
}
}
func TestLimiter(t *testing.T) {
l := newLimiter()
now := time.Now()
// 同一秒内第 21 次拒绝(QPS)
pass := 0
for i := 0; i < 25; i++ {
if l.allow("k", now) {
pass++
}
}
if pass != LimitQPS {
t.Fatalf("QPS 应放行 %d,实际 %d", LimitQPS, pass)
}
// 后续秒窗重置但 60 秒窗累计:S2 放 20、S3 放 20(累计 60),S4 拒绝(QPM)
for _, off := range []time.Duration{2 * time.Second, 3 * time.Second} {
pass2 := 0
for i := 0; i < 25; i++ {
if l.allow("k", now.Add(off)) {
pass2++
}
}
if pass2 != 20 {
t.Fatalf("+%v 应放行 20(QPS),实际 %d", off, pass2)
}
}
if l.allow("k", now.Add(4*time.Second)) {
t.Fatalf("60 秒窗口内累计达 60 应拒绝")
}
// 不同 key 相互独立
if !l.allow("other", now.Add(3*time.Second)) {
t.Fatal("不同 key 不应被牵连")
}
// 滑动窗口过期后恢复
l2 := newLimiter()
t0 := time.Now()
for i := 0; i < LimitQPM; i++ {
if !l2.allow("k2", t0.Add(time.Duration(i)*time.Second)) {
t.Fatalf("分散 60 秒内应全部放行(第 %d 次被拒)", i+1)
}
}
if l2.allow("k2", t0.Add(59*time.Second)) {
t.Fatal("60 次命中仍在窗口内应被拒")
}
if !l2.allow("k2", t0.Add(130*time.Second)) {
t.Fatal("窗口滑过后应恢复放行")
}
}
func TestNormalizePath(t *testing.T) {
if normalizePath("api/users/") != "/api/users" {
t.Fatalf("got %q", normalizePath("api/users/"))
}
if normalizePath("") != "/" {
t.Fatalf("空路径 → /")
}
if normalizePath("/x/") != "/x" {
t.Fatalf("去尾斜杠")
}
_ = strings.TrimSpace
}
func TestResponseBodyFormats(t *testing.T) {
for _, body := range []string{"@cname", `{"name":"@cname"}`, "plain text\n"} {
if got := responseBody("text/plain", body); got != body {
t.Fatalf("text changed: %q", got)
}
}
var decoded map[string]int
got := responseBody("application/json; charset=utf-8", `{"n":"@integer(1,1)"}`)
if err := json.Unmarshal([]byte(got), &decoded); err != nil || decoded["n"] != 1 {
t.Fatalf("JSON template not expanded: %q", got)
}
}