From e966061025dc50ee46880e7ff4e3389e90129760 Mon Sep 17 00:00:00 2001 From: k2safe Date: Sun, 20 Sep 2026 20:43:21 +0800 Subject: [PATCH] test: enforce chinese schema comments --- docs/07-data-model.md | 2 +- docs/reports/phase-7-regression-status.md | 1 + .../storage/mysql/no_foreign_keys_test.go | 58 +++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/docs/07-data-model.md b/docs/07-data-model.md index 101fe75..4659827 100644 --- a/docs/07-data-model.md +++ b/docs/07-data-model.md @@ -11,7 +11,7 @@ - `internal/storage/mysql/`:连接、事务与显式迁移,不向 SDK 暴露。 - `sdk/go/`:独立 Go module;对外 DTO 与服务端模型分别维护。 -模型使用普通 Go struct 与 `db` 标签,模型与持久化字段提供中文注释,SQL 表和字段同步维护中文 COMMENT。SQL 是可审查的版本化迁移。目前未引入 ORM,也没有自动同步表结构。 +模型使用普通 Go struct 与 `db` 标签,模型与持久化字段提供中文注释,SQL 表和字段同步维护中文 COMMENT。`TestFreshInstallBaselineHasChineseModelComments` 会在测试门禁中逐表、逐字段检查当前基线,防止新增持久化字段漏写说明。SQL 是可审查的版本化迁移。目前未引入 ORM,也没有自动同步表结构。 ## 2. 存储约定 diff --git a/docs/reports/phase-7-regression-status.md b/docs/reports/phase-7-regression-status.md index 3450407..8809e43 100644 --- a/docs/reports/phase-7-regression-status.md +++ b/docs/reports/phase-7-regression-status.md @@ -9,6 +9,7 @@ | 本地发布门禁统一入口 | 2026-09-20;`make release-local-check` | PASS;服务端、SDK、独立示例竞态测试、vet、build、当前协议门禁、146个 Markdown 本地链接和 SDK 独立依赖检查均通过;命令明确不替代真实 MySQL 集成与目标生产验收 | | 就绪探针排空门控 | `go test -race ./internal/transport/httpserver ./cmd/orbit`;`TestReadinessEntersDrainBeforeShutdown` | PASS;SIGTERM 关闭路径先将 `/health/ready` 置为 503,再进入限定优雅关闭窗口;响应不泄露内部错误 | | 健康探针响应契约 | 2026-09-20;`go test ./internal/transport/httpserver && go test -race ./internal/transport/httpserver && make docs-check` | PASS;`/health/live` 与 `/health/ready` 返回 `Cache-Control: no-store`、`X-Content-Type-Options: nosniff`;未就绪和排空状态返回 `Retry-After: 1`,并由 HTTP 契约测试覆盖 | +| 数据模型中文注释门禁 | 2026-09-20;`go test ./internal/storage/mysql -run 'Test(FreshInstallBaselineHasChineseModelComments|FreshInstallBaselineContainsNoReferenceDDL|MigrationsAfterForeignKeyRemovalDoNotReintroduceReferences|PostRemovalMigrationGuard)$'` | PASS;当前无外键基线逐表、逐持久化字段检查中文 `COMMENT`,并继续阻止后续迁移重新引入引用约束 | | 服务端单元与竞态、vet、构建 | Docker `golang:1.27.1`,仓库声明版本;`GOWORK=off go test -race ./... && go vet ./... && go build ./...` | PASS | | 后台运行参数边界 | `internal/config.TestRuntimeDefaultsAndBoundedOverrides`;默认值与五个 `ORBIT_*` 覆盖项 | PASS;循环间隔限定100ms–1m,批量限定1–100,排空超时限定1s–5m,服务启动日志记录生效值 | | 生效运行参数指标 | `internal/observability.TestRuntimeObservationDoesNotExposeErrors`;固定指标目录 | PASS;workflow/schedule/execution_recovery 间隔、批量上限和排空超时以固定基数指标导出;排空状态同步为固定 gauge,不进入业务或租户标签 | diff --git a/internal/storage/mysql/no_foreign_keys_test.go b/internal/storage/mysql/no_foreign_keys_test.go index bbab21a..83c91be 100644 --- a/internal/storage/mysql/no_foreign_keys_test.go +++ b/internal/storage/mysql/no_foreign_keys_test.go @@ -4,6 +4,7 @@ import ( "regexp" "strings" "testing" + "unicode" ) // 已应用的历史文件不改写;移除迁移之后的DDL不能再引入数据库引用约束。 @@ -52,6 +53,63 @@ func TestFreshInstallBaselineContainsNoReferenceDDL(t *testing.T) { } } +// 当前基线是持久化模型的可执行说明:表和每个字段都必须保留中文注释, +// 防止后续新增模型只写类型、不写职责、单位和可空语义。 +func TestFreshInstallBaselineHasChineseModelComments(t *testing.T) { + inTable := false + tableName := "" + columns := 0 + for lineNumber, line := range strings.Split(freshInstallBaseline, "\n") { + trimmed := strings.TrimSpace(line) + if strings.HasPrefix(trimmed, "CREATE TABLE `") { + if inTable { + t.Fatalf("line %d starts a table before %s was closed", lineNumber+1, tableName) + } + start := strings.Index(trimmed, "`") + 1 + end := strings.Index(trimmed[start:], "`") + if start <= 0 || end < 0 { + t.Fatalf("line %d has malformed table declaration: %s", lineNumber+1, trimmed) + } + tableName = trimmed[start : start+end] + inTable = true + columns = 0 + continue + } + if !inTable { + continue + } + if strings.HasPrefix(trimmed, "`") { + columns++ + comment := strings.Index(line, " COMMENT '") + if comment < 0 || !hasChineseComment(line[comment:]) { + t.Fatalf("table %s column on line %d must have a Chinese COMMENT", tableName, lineNumber+1) + } + } + if strings.HasPrefix(trimmed, ") ENGINE=") { + if columns == 0 { + t.Fatalf("table %s has no columns", tableName) + } + if !strings.Contains(line, " COMMENT='") || !hasChineseComment(line) { + t.Fatalf("table %s must have a Chinese table COMMENT", tableName) + } + inTable = false + tableName = "" + } + } + if inTable { + t.Fatalf("table %s is not closed", tableName) + } +} + +func hasChineseComment(s string) bool { + for _, r := range s { + if unicode.Is(unicode.Han, r) { + return true + } + } + return false +} + func TestPostRemovalMigrationGuard(t *testing.T) { if err := validateNoReferenceDDL(9, []string{"ALTER TABLE pulse_tasks ADD CONSTRAINT fk FOREIGN KEY (queue_id) REFERENCES pulse_queues(id)"}); err == nil { t.Fatal("post-removal migration accepted foreign-key DDL")