feat(logging): 增强测试框架的日志记录功能

- 在 GenerateSwagger 方法中添加调试日志记录,记录部分运行检查和现有端点的保留情况
- 更新 Action 方法以支持测试模式下的日志缓冲,确保日志记录的准确性
- 删除不再使用的计划文档,简化项目结构
This commit is contained in:
2026-04-04 01:57:10 +08:00
parent 17e1090077
commit 63360f9a14
3 changed files with 43 additions and 69 deletions
@@ -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(...)`
+4
View File
@@ -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 {
+39
View File
@@ -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 != "" {