From d0d0ce38aa859ce9eb7c10fd892b70936236ef21 Mon Sep 17 00:00:00 2001 From: hoteas <925970985@qq.com> Date: Sat, 4 Apr 2026 01:57:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(logging):=20=E5=A2=9E=E5=BC=BA=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=A1=86=E6=9E=B6=E7=9A=84=E6=97=A5=E5=BF=97=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 GenerateSwagger 方法中添加调试日志记录,记录部分运行检查和现有端点的保留情况 - 更新 Action 方法以支持测试模式下的日志缓冲,确保日志记录的准确性 - 删除不再使用的计划文档,简化项目结构 --- .../plans/补充_note_方法示例_d1af0e5b.plan.md | 69 ------------------- db/transaction.go | 4 ++ testing_swagger.go | 39 +++++++++++ 3 files changed, 43 insertions(+), 69 deletions(-) delete mode 100644 .cursor/plans/补充_note_方法示例_d1af0e5b.plan.md diff --git a/.cursor/plans/补充_note_方法示例_d1af0e5b.plan.md b/.cursor/plans/补充_note_方法示例_d1af0e5b.plan.md deleted file mode 100644 index 54dee3c..0000000 --- a/.cursor/plans/补充_note_方法示例_d1af0e5b.plan.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -name: 补充 Note 方法示例 -overview: 在 Testing_API测试框架.md 的"第二步:编写测试用例"部分补充 Note 方法的使用示例和说明,同步更新 SKILL.md。 -todos: - - id: doc-step2-example - content: 文档:在第二步的完整订单示例中加入 Note 调用示范 - status: completed - - id: doc-note-section - content: 文档:在用例编写范式中新增 Note 使用说明(典型场景 + 示例 + 何时必须用) - status: completed - - id: doc-hotimev15-sync - content: 同步修改 hotimev1.5 中的 Testing_API测试框架.md - status: completed - - id: skill-note-guide - content: SKILL.md:补充 Note 使用指引和更多场景示例 - status: completed -isProject: false ---- - -# 补充 Note 方法示例到文档和 Skill - -## 现状分析 - -目前 `Note` 方法在文档中的存在情况: - -- [链式 API 参考](d:\work\xbc\docs\Testing_API测试框架.md) 第 474、494 行:仅出现在 API 表格中,一句话描述 -- [API 速查表](d:\work\xbc\docs\Testing_API测试框架.md) 第 662-668 行:只有一个简短示例(`sign = MD5(...)`) -- [SKILL.md](C:\Users\92597.cursor\skills\hotime-tdd-testing\SKILL.md) 第 195-197 行:一行示例 - -**缺失**:"第二步:编写测试用例"(第 66-149 行)的完整订单示例中完全没有出现 `Note`,而这是新手学习的第一个入口。 - -## 需要修改的内容 - -### 1. 文档:第二步示例中加入 Note 使用 - -在 [Testing_API测试框架.md](d:\work\xbc\docs\Testing_API测试框架.md) 第 80-148 行的订单创建示例中,在**正确请求**部分加入 `Note` 调用,展示以下几种典型场景: - -- 在正确请求处用 `Note` 说明参数值含义(如 `quantity=3` 是故意的用于校验总价 `29.9*3=89.7`) - -### 2. 文档:用例编写范式中新增 Note 独立小节 - -在"用例编写范式"部分(第 219 行之后),在"错误用例"之前或之后,新增一个关于 Note 的说明段落或小节,覆盖以下典型使用场景: - -- **参数值参考**:说明某个参数值的来源或含义,如 `Note("status: 0=待付款, 1=已付款, 2=已发货, 3=已完成")` -- **算法说明**:简要描述接口涉及的签名/加密算法,如 `Note("sign = MD5(smsProxyKey+timestamp)")` -- **接口/用例说明**:补充 desc 无法涵盖的上下文,如 `Note("需先调用 /api/cart/add 加入购物车")` 或 `Note("此接口支持批量操作,最多100条")` -- 在用例编写范式表格中增加 Note 的定位说明 - -### 3. 文档:同步更新 hotimev1.5 中的同名文件 - -[hotimev1.5/docs/Testing_API测试框架.md](d:\work\hotimev1.5\docs\Testing_API测试框架.md) 需要做相同的修改。 - -### 4. Skill 文件:补充 Note 指引 - -在 [SKILL.md](C:\Users\92597.cursor\skills\hotime-tdd-testing\SKILL.md) 中: - -- 在 Phase 1 的 1.3 小节之后(或 1.2 和 1.3 之间),新增 Note 的使用指引 -- 说明何时应该使用 Note(有必要的场景),给出 2-3 个典型示例 -- 在 API 速查的"备注"部分补充更多场景示例 - -## 修改要点 - -- Note 不改变测试逻辑,只是给调试控制台和 api-spec.json 添加可读说明 -- Note 是可选的,但在以下场景**建议必须**使用: - - 参数中有枚举值(如 status、type)需要说明各值含义 - - 涉及签名/加密等算法的接口 - - 接口有前置依赖或特殊调用顺序 -- Note 可以放在链式调用的任意位置(`a.Note(...)` 或 `c.Note(...)`) - diff --git a/db/transaction.go b/db/transaction.go index 65a57df..8d4922c 100644 --- a/db/transaction.go +++ b/db/transaction.go @@ -12,6 +12,9 @@ var savepointCounter uint64 // 如果 action 返回 true 则提交事务;返回 false 则回滚 // 测试模式下(testTx != nil),使用 SAVEPOINT 代替真事务,确保嵌套事务不会绕过外层测试事务 func (that *HoTimeDB) Action(action func(db HoTimeDB) (isSuccess bool)) (isSuccess bool) { + that.testLogBufMu.Lock() + logBuf := that.testLogBuf + that.testLogBufMu.Unlock() db := HoTimeDB{ DB: that.DB, ContextBase: that.ContextBase, @@ -33,6 +36,7 @@ func (that *HoTimeDB) Action(action func(db HoTimeDB) (isSuccess bool)) (isSucce limitMu: sync.Mutex{}, testTx: that.testTx, testMu: that.testMu, + testLogBuf: logBuf, } if that.testTx != nil { diff --git a/testing_swagger.go b/testing_swagger.go index 57d4138..2f2834e 100644 --- a/testing_swagger.go +++ b/testing_swagger.go @@ -275,6 +275,15 @@ func (that *TestApp) GenerateSwagger(title, version, outputDir string) error { } isPartialRun := visitedPaths > 0 && visitedPaths < totalPaths + // #region agent log + func() { + if f, err := os.OpenFile("debug-e1b6ef.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil { + fmt.Fprintf(f, `{"sessionId":"e1b6ef","hypothesisId":"D","message":"partial_run_check","data":{"proj":"%s","totalPaths":%d,"visitedPaths":%d,"isPartialRun":%v}}`+"\n", projName, totalPaths, visitedPaths, isPartialRun) + f.Close() + } + }() + // #endregion + // 仅部分运行时才加载已有 spec 用于合并,全量运行时清空重建 var existingEndpoints map[string]map[string]interface{} if isPartialRun { @@ -302,10 +311,40 @@ func (that *TestApp) GenerateSwagger(title, version, outputDir string) error { // 部分运行且该路径未被访问 → 保留已有 spec 中的数据 if isPartialRun && !visited[apiPath] { if existing, ok := existingEndpoints[apiPath]; ok { + // #region agent log + if strings.Contains(apiPath, "batch") { + func() { + if f, err := os.OpenFile("debug-e1b6ef.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil { + fmt.Fprintf(f, `{"sessionId":"e1b6ef","hypothesisId":"D","message":"kept_existing","data":{"path":"%s"}}`+"\n", apiPath) + f.Close() + } + }() + } + // #endregion endpoints = append(endpoints, existing) continue } + // #region agent log + if strings.Contains(apiPath, "batch") { + func() { + if f, err := os.OpenFile("debug-e1b6ef.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil { + fmt.Fprintf(f, `{"sessionId":"e1b6ef","hypothesisId":"D","message":"NOT_in_existing_file","data":{"path":"%s"}}`+"\n", apiPath) + f.Close() + } + }() + } + // #endregion } + // #region agent log + if strings.Contains(apiPath, "batch") && isPartialRun && visited[apiPath] { + func() { + if f, err := os.OpenFile("debug-e1b6ef.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil { + fmt.Fprintf(f, `{"sessionId":"e1b6ef","hypothesisId":"D","message":"regenerated","data":{"path":"%s","numRecs":%d}}`+"\n", apiPath, len(recs)) + f.Close() + } + }() + } + // #endregion httpMethod := "POST" if len(recs) > 0 && recs[0].Method != "" {