From 89434e4f99d9a9a21431750d584be8902b649fc0 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Sun, 4 Oct 2026 10:54:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(contract):=20enums=20=E7=9A=84=20MODES=20?= =?UTF-8?q?=E6=BC=82=E7=A7=BB=E5=AE=88=E5=8D=AB=E6=94=B9=E6=8C=89=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E6=94=B6=E9=9B=86=EF=BC=8C=E6=8A=97=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E6=8B=86=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TestModeTableMatchesFrontendModes 原先写死读 frontend/web/src/lib/types.ts;阶段 0 把该 文件拆成 barrel + lib/types/*.ts 后 MODES 住进了 types/chat.ts ⇒ 测试红。 改为读 barrel + 目录下全部 .ts(新增 frontendTypesDir 常量与 readFrontendTypes 辅助): 「MODES 住哪个文件」不再是本测试的耦合点,以后再搬也不用改它;barrel 缺失 (backend-only checkout)仍按 fs.ErrNotExist 跳过。 验证:go test ./internal/contract/enums/ 绿;全树 go test ./... = 58 包 ok / 0 FAIL; gofmt -l 干净。 --- internal/contract/enums/enums_test.go | 46 ++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/internal/contract/enums/enums_test.go b/internal/contract/enums/enums_test.go index 0768c24..6738dbb 100644 --- a/internal/contract/enums/enums_test.go +++ b/internal/contract/enums/enums_test.go @@ -18,6 +18,7 @@ import ( "regexp" "slices" "sort" + "strings" "testing" "git.yeij.top/AskaEth/Cyrene_v2/internal/contract/enums" @@ -128,23 +129,52 @@ func TestMoodTableMatchesEmotionBuiltins(t *testing.T) { } } +// frontendTypesDir 是前端共享类型的目录:barrel(lib/types.ts)+ 按域拆分的 +// lib/types/*.ts(阶段 0 的结构拆分,方案见 DevDocs/audit/_plan.md)。 +const frontendTypesDir = "../../../frontend/web/src/lib/types" + +// readFrontendTypes 汇总 barrel 与目录下全部 .ts 的源码。按目录收集而不是写死单个文件, +// 是为了让「MODES 数组住在哪个文件」不成为本测试的耦合点——以后再搬一次也不用改它。 +// barrel 缺失(backend-only checkout)时返回 fs.ErrNotExist,由调用方跳过。 +func readFrontendTypes() ([]byte, error) { + paths := []string{frontendTypesDir + ".ts"} + entries, err := os.ReadDir(frontendTypesDir) + if err != nil && !errors.Is(err, fs.ErrNotExist) { + return nil, err + } + for _, e := range entries { + if !e.IsDir() && strings.HasSuffix(e.Name(), ".ts") { + paths = append(paths, frontendTypesDir+"/"+e.Name()) + } + } + var all []byte + for _, p := range paths { + raw, err := os.ReadFile(p) + if err != nil { + return nil, err + } + all = append(all, raw...) + all = append(all, '\n') + } + return all, nil +} + // TestModeTableMatchesFrontendModes asserts the mode table matches the frontend's -// Mode union / MODES array (frontend/web/src/lib/types.ts), the only code-level -// definition of the nine modes. This is the cross-language drift guard: adding a -// mode on either side without the other fails here. +// Mode union / MODES array, the only code-level definition of the nine modes. This +// is the cross-language drift guard: adding a mode on either side without the other +// fails here. func TestModeTableMatchesFrontendModes(t *testing.T) { t.Parallel() - const typesPath = "../../../frontend/web/src/lib/types.ts" - raw, err := os.ReadFile(typesPath) + raw, err := readFrontendTypes() if err != nil { if errors.Is(err, fs.ErrNotExist) { - t.Skipf("%s not present (backend-only checkout)", typesPath) + t.Skipf("%s not present (backend-only checkout)", frontendTypesDir) } - t.Fatalf("read %s: %v", typesPath, err) + t.Fatalf("read frontend types: %v", err) } block := regexp.MustCompile(`(?s)export const MODES\s*:[^=]*=\s*\[(.*?)\]`).FindSubmatch(raw) if block == nil { - t.Fatalf("MODES array not found in %s", typesPath) + t.Fatalf("MODES array not found under %s", frontendTypesDir) } quoted := regexp.MustCompile(`"([^"]+)"`).FindAllStringSubmatch(string(block[1]), -1) frontend := make([]string, 0, len(quoted))