package jscriptx // 这是**内部测试**(package jscriptx,不是 jscriptx_test):有几条要读 // Engine.loader 这个未导出字段、调 isPrepared 这个未导出函数,验的是"多层 // Loader 合成之后引擎实际拿到的是什么",从外面看不见。 import ( "errors" "fmt" "strings" "testing" ) // srcLoader 是个最简单的内存 Loader,用来搭叠层测试。 type srcLoader struct { src map[string]string ver map[string]string prepared bool names []string rebuilt int fail error // 非 ErrScriptNotFound 的故障 } func (l *srcLoader) Load(name string) (string, string, error) { if l.fail != nil { return "", "", l.fail } s, ok := l.src[name] if !ok { return "", "", fmt.Errorf("%w: %s", ErrScriptNotFound, name) } return s, l.ver[name], nil } func (l *srcLoader) Prepared() bool { return l.prepared } func (l *srcLoader) Names() []string { return l.names } func (l *srcLoader) Rebuild() error { l.rebuilt++; return nil } func clsOf(v string) string { return "export default class C { V() { return " + `"` + v + `"` + " } }" } func TestOverlay_后面的盖前面的(t *testing.T) { base := &srcLoader{src: map[string]string{"A": clsOf("base-A"), "B": clsOf("base-B")}} over := &srcLoader{src: map[string]string{"A": clsOf("over-A"), "C": clsOf("over-C")}} o, err := Overlay(base, over) if err != nil { t.Fatal(err) } for _, tc := range []struct{ name, want string }{ {"A", "over-A"}, // 两层都有,取后面那层 {"B", "base-B"}, // 只有前面那层有 {"C", "over-C"}, // 只有后面那层有 } { src, _, err := o.Load(tc.name) if err != nil { t.Fatalf("%s: %v", tc.name, err) } if !strings.Contains(src, tc.want) { t.Errorf("%s 取到的不是 %s: %s", tc.name, tc.want, src) } } } func TestOverlay_都没有时报脚本不存在(t *testing.T) { o, err := Overlay(&srcLoader{}, &srcLoader{}) if err != nil { t.Fatal(err) } _, _, err = o.Load("X") if !errors.Is(err, ErrScriptNotFound) { t.Fatalf("要能被 ErrScriptNotFound 匹配上: %v", err) } if !strings.Contains(err.Error(), "X") { t.Errorf("报错里要带脚本名: %v", err) } } // 某一层出故障(不是"脚本不存在")时要立刻报出来, // 不能被下面那层的结果悄悄盖掉——否则线上换了实现都不知道。 func TestOverlay_故障不被下层掩盖(t *testing.T) { boom := errors.New("数据库连不上") base := &srcLoader{src: map[string]string{"A": clsOf("base-A")}} over := &srcLoader{fail: boom} o, _ := Overlay(base, over) _, _, err := o.Load("A") if !errors.Is(err, boom) { t.Fatalf("故障被吞了: %v", err) } } // 版本号要带上层号:覆盖层撤掉后落回下层,两层版本号万一撞上, // 不带层号引擎就看不出脚本已经换了人。 func TestOverlay_版本号带层号(t *testing.T) { base := &srcLoader{ src: map[string]string{"A": clsOf("base-A")}, ver: map[string]string{"A": "v1"}, } over := &srcLoader{ src: map[string]string{"A": clsOf("over-A")}, ver: map[string]string{"A": "v1"}, // 故意跟 base 撞 } o, _ := Overlay(base, over) _, v1, _ := o.Load("A") delete(over.src, "A") // 撤掉覆盖 _, v2, _ := o.Load("A") if v1 == v2 { t.Fatalf("落回下层后版本号没变(都是 %q),引擎会继续用旧的编译结果", v1) } } // 成员不给版本号时要原样透传空串,让引擎去按源码算哈希。 func TestOverlay_空版本号原样透传(t *testing.T) { o, _ := Overlay(&srcLoader{src: map[string]string{"A": clsOf("a")}}) if _, v, _ := o.Load("A"); v != "" { t.Errorf("版本号该是空的,拿到 %q", v) } } func TestOverlay_Prepared必须一致(t *testing.T) { yes := &srcLoader{prepared: true} no := &srcLoader{prepared: false} if _, err := Overlay(yes, no); err == nil { t.Error("混着来该报错") } if _, err := Overlay(yes, yes); err != nil { t.Errorf("都是打好包的该通过: %v", err) } o, err := Overlay(no, no) if err != nil { t.Fatalf("都是原始源码该通过: %v", err) } if o.Prepared() { t.Error("Prepared 该跟成员一致") } } func TestOverlay_参数校验(t *testing.T) { if _, err := Overlay(); err == nil { t.Error("一个都不给该报错") } if _, err := Overlay(&srcLoader{}, nil); err == nil { t.Error("给 nil 该报错") } } func TestOverlay_Names去重排序(t *testing.T) { base := &srcLoader{names: []string{"B/b", "A/a"}} over := &srcLoader{names: []string{"A/a", "C/c"}} plain := LoaderFunc(func(string) (string, string, error) { return "", "", nil }) // 没有 Names o, _ := Overlay(base, over, plain) got := strings.Join(o.Names(), ",") if got != "A/a,B/b,C/c" { t.Errorf("Names() = %s", got) } } func TestOverlay_Rebuild走遍每一层(t *testing.T) { base, over := &srcLoader{}, &srcLoader{} o, _ := Overlay(base, over, LoaderFunc(func(string) (string, string, error) { return "", "", nil })) if err := o.Rebuild(); err != nil { t.Fatal(err) } if base.rebuilt != 1 || over.rebuilt != 1 { t.Errorf("每层都该重建一次: base=%d over=%d", base.rebuilt, over.rebuilt) } } // WithLoader 给多个时自动叠起来,跟 Overlay 的语义一致。 func TestWithLoader_多个自动叠层(t *testing.T) { base := &srcLoader{src: map[string]string{"A": clsOf("base-A"), "B": clsOf("base-B")}} over := &srcLoader{src: map[string]string{"A": clsOf("over-A")}} e, err := New(WithLoader(base, over)) if err != nil { t.Fatal(err) } defer e.Close() mustCall := func(name, want string) { t.Helper() s, err := e.Script(name) if err != nil { t.Fatal(err) } got, err := s.Call(t.Context(), "V") if err != nil { t.Fatal(err) } if got != want { t.Errorf("%s = %v, 想要 %s", name, got, want) } } mustCall("A", "over-A") mustCall("B", "base-B") } // 多次调用 WithLoader 继续往后叠,效果跟一次传多个一样。 func TestWithLoader_多次调用继续叠(t *testing.T) { base := &srcLoader{src: map[string]string{"A": clsOf("base-A")}} over := &srcLoader{src: map[string]string{"A": clsOf("over-A")}} e, err := New(WithLoader(base), WithLoader(over)) if err != nil { t.Fatal(err) } defer e.Close() s, err := e.Script("A") if err != nil { t.Fatal(err) } if got, _ := s.Call(t.Context(), "V"); got != "over-A" { t.Errorf("后面那次该盖住前面的,拿到 %v", got) } } // 只给一个时不该套上 Overlay,Prepared 之类的行为要原样保留。 func TestWithLoader_单个不套壳(t *testing.T) { l := &srcLoader{prepared: true} e, err := New(WithLoader(l)) if err != nil { t.Fatal(err) } defer e.Close() if e.loader != Loader(l) { t.Errorf("单个 Loader 该原样用,拿到 %T", e.loader) } } func TestWithLoader_层间Prepared不一致时New报错(t *testing.T) { _, err := New(WithLoader(&srcLoader{prepared: true}, &srcLoader{prepared: false})) if err == nil { t.Fatal("该报错") } if !strings.Contains(err.Error(), "Prepared") { t.Errorf("报错要说清楚是 Prepared 不一致: %v", err) } } // tagged 是带版本标签的内存 Loader。 type tagged struct { *srcLoader ver string } func (t *tagged) Version() string { return t.ver } func TestVersion_点名要哪一层(t *testing.T) { v1 := &tagged{&srcLoader{src: map[string]string{"A": clsOf("v1-A"), "OnlyV1": clsOf("x")}}, "v1"} v2 := &tagged{&srcLoader{src: map[string]string{"A": clsOf("v2-A")}}, "v2"} o, err := Overlay(v1, v2) if err != nil { t.Fatal(err) } for _, tc := range []struct{ name, want string }{ {"A", "v2-A"}, // 不点名:上层盖下层 {"A@v2", "v2-A"}, // 点名上层 {"A@v1", "v1-A"}, // 点名下层 {"OnlyV1@v1", "x"}, // 点名的那层独有的 } { src, _, err := o.Load(tc.name) if err != nil { t.Fatalf("%s: %v", tc.name, err) } if !strings.Contains(src, tc.want) { t.Errorf("%s 该拿到 %s: %s", tc.name, tc.want, src) } } } // 点名是"只认这一层":那层没有就报错,不能掉到别的层去。 func TestVersion_点名后不回落(t *testing.T) { v1 := &tagged{&srcLoader{src: map[string]string{"A": clsOf("v1-A")}}, "v1"} v2 := &tagged{&srcLoader{}, "v2"} // 空的 o, _ := Overlay(v1, v2) _, _, err := o.Load("A@v2") if !errors.Is(err, ErrScriptNotFound) { t.Fatalf("v2 层没有 A,该报不存在而不是回落到 v1: %v", err) } } func TestVersion_点名不存在的版本(t *testing.T) { v1 := &tagged{&srcLoader{src: map[string]string{"A": clsOf("a")}}, "v1"} o, _ := Overlay(v1) _, _, err := o.Load("A@v9") if !errors.Is(err, ErrScriptNotFound) { t.Fatalf("该报不存在: %v", err) } if !strings.Contains(err.Error(), "v1") { t.Errorf("报错该提示有哪些版本: %v", err) } } // 点名不同的层,版本号里的层号要跟着变,两份实现才能各自缓存。 func TestVersion_点名时版本号带层号(t *testing.T) { v1 := &tagged{&srcLoader{ src: map[string]string{"A": clsOf("v1-A")}, ver: map[string]string{"A": "same"}, }, "v1"} v2 := &tagged{&srcLoader{ src: map[string]string{"A": clsOf("v2-A")}, ver: map[string]string{"A": "same"}, // 故意撞 }, "v2"} o, _ := Overlay(v1, v2) _, a, _ := o.Load("A@v1") _, b, _ := o.Load("A@v2") if a == b { t.Fatalf("两层的版本号该区分开,都是 %q", a) } } func TestVersion_标签校验(t *testing.T) { a := &tagged{&srcLoader{}, "v1"} b := &tagged{&srcLoader{}, "v1"} if _, err := Overlay(a, b); err == nil { t.Error("标签重名该报错") } bad := &tagged{&srcLoader{}, "v@1"} if _, err := Overlay(bad); err == nil { t.Error("标签里带分隔符该报错") } } func TestVersion_Tag给任意Loader贴标签(t *testing.T) { plain := &srcLoader{src: map[string]string{"A": clsOf("plain-A")}} other := &srcLoader{src: map[string]string{"A": clsOf("other-A")}} o, err := Overlay(plain, Tag("hotfix", other)) if err != nil { t.Fatal(err) } src, _, err := o.Load("A@hotfix") if err != nil { t.Fatal(err) } if !strings.Contains(src, "other-A") { t.Errorf("该拿到贴了标签那层的: %s", src) } if got := strings.Join(o.Versions(), ","); got != "hotfix" { t.Errorf("Versions() = %s", got) } } // 贴标签不该改变底下 Loader 的 Prepared,否则叠层的一致性校验会误判。 func TestVersion_Tag透传Prepared(t *testing.T) { if !isPrepared(Tag("v1", &srcLoader{prepared: true})) { t.Error("底下是 prepared 的,贴完标签该还是") } if isPrepared(Tag("v1", &srcLoader{prepared: false})) { t.Error("底下不是 prepared 的,贴完标签不该变成是") } // 一层贴了标签一层没贴,Prepared 一致就该能叠 if _, err := Overlay(&srcLoader{prepared: true}, Tag("v1", &srcLoader{prepared: true})); err != nil { t.Errorf("该能叠: %v", err) } } func TestVersion_拆名字(t *testing.T) { for _, tc := range []struct { in, bare, ver string ok bool }{ {"Foo/Bar@v1", "Foo/Bar", "v1", true}, {"Foo/Bar", "Foo/Bar", "", false}, {"@v1", "@v1", "", false}, // 左边空着,不算点名 {"Foo@", "Foo@", "", false}, // 右边空着,不算点名 {"a@b@v1", "a@b", "v1", true}, // 用最后一个分隔符 } { bare, ver, ok := splitVersion(tc.in) if bare != tc.bare || ver != tc.ver || ok != tc.ok { t.Errorf("splitVersion(%q) = %q,%q,%v;想要 %q,%q,%v", tc.in, bare, ver, ok, tc.bare, tc.ver, tc.ok) } } } // 走完整链路:从 Engine 点名要某个版本。 func TestVersion_从Engine点名(t *testing.T) { v1 := &tagged{&srcLoader{src: map[string]string{"A": clsOf("v1-A")}}, "v1"} v2 := &tagged{&srcLoader{src: map[string]string{"A": clsOf("v2-A")}}, "v2"} e, err := New(WithLoader(v1, v2)) if err != nil { t.Fatal(err) } defer e.Close() call := func(name string) any { t.Helper() s, err := e.Script(name) if err != nil { t.Fatal(err) } got, err := s.Call(t.Context(), "V") if err != nil { t.Fatal(err) } return got } if got := call("A"); got != "v2-A" { t.Errorf("不点名该拿到上层: %v", got) } if got := call("A@v1"); got != "v1-A" { t.Errorf("点名 v1 该拿到 v1: %v", got) } // 两个版本各自缓存,互不干扰 if got := call("A@v2"); got != "v2-A" { t.Errorf("点名 v2 该拿到 v2: %v", got) } }