mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-10 16:53:35 +08:00
- SQL 导出:formatSQLValue 按方言转义反斜杠,修复 MySQL 系 dump 还原时静默改写数据、 且以反斜杠结尾的值吞掉闭合引号致源库恶意行可执行任意 SQL 的问题;非 MySQL 方言保持原样 - 明文凭据:daily_secrets.json 由 0o644 改为 0o600、目录改 0o700,并对历史文件显式 Chmod - SSH 隧道:RegisterSSHNetwork 改为确定性 network 名并复用缓存客户端,消除驱动全局 dialer 表随重连线性增长、永久钉住 ssh.Client 的连接与 goroutine 泄漏 - xlsx 导入:单元格 r 属性列号增加 OOXML 16384 上限并提前熔断,避免篡改文件放大分配致 OOM - 数据导出:三处非 SQL 导出入口捕获 file.Close 错误,不再在落盘失败时返回“导出成功” - 数据根迁移:copyFile 捕获 Close 错误并补 Sync,避免被截断的副本被判定为迁移成功 - 执行计划:节点 ID 改为按 ExplainResult 派生,移除进程级全局计数器与 reset, 修复并发诊断互相踩踏编号导致的重复 node ID 与错挂父子边 - 补充 5 个回归测试文件,含“导出→切分”闭环断言与未转义时的反向对照
97 lines
3.7 KiB
Go
97 lines
3.7 KiB
Go
package app
|
||
|
||
import (
|
||
"fmt"
|
||
"sync"
|
||
"testing"
|
||
|
||
"GoNavi-Wails/internal/connection"
|
||
)
|
||
|
||
// TestAppendExplainChildNumbersPerResult 覆盖节点 ID 的按解析隔离。
|
||
//
|
||
// 回归背景:节点 ID 原先由包级全局计数器 explainNodeIDCounter 生成,每个方言解析器在入口
|
||
// 调用 resetExplainNodeID() 归零。DiagnoseQuery 是 Wails 绑定方法,每个前端调用都在独立
|
||
// goroutine 中派发,两次并发诊断会互相踩踏该计数器,导致同一份 ExplainResult 内出现重复
|
||
// node ID、Edges 的 From/To 指向歧义节点、前端计划图渲染错乱。
|
||
func TestAppendExplainChildNumbersPerResult(t *testing.T) {
|
||
var first connection.ExplainResult
|
||
rootID := appendExplainChild(&first, "", connection.ExplainNode{OpDetail: "root"})
|
||
childID := appendExplainChild(&first, rootID, connection.ExplainNode{OpDetail: "child"})
|
||
grandID := appendExplainChild(&first, childID, connection.ExplainNode{OpDetail: "grand"})
|
||
|
||
if rootID != "n1" || childID != "n2" || grandID != "n3" {
|
||
t.Fatalf("编号序列 = %s/%s/%s,期望 n1/n2/n3", rootID, childID, grandID)
|
||
}
|
||
|
||
// 另一份 result 必须从 n1 重新开始,不受前一份影响。
|
||
var second connection.ExplainResult
|
||
if got := appendExplainChild(&second, "", connection.ExplainNode{OpDetail: "root"}); got != "n1" {
|
||
t.Fatalf("第二份 result 的首个节点 ID = %s,期望 n1(编号未按 result 隔离)", got)
|
||
}
|
||
|
||
// Edge 必须指向正确的父子节点。
|
||
if len(first.Edges) != 2 {
|
||
t.Fatalf("Edges 数量 = %d,期望 2", len(first.Edges))
|
||
}
|
||
if first.Edges[0].From != "n1" || first.Edges[0].To != "n2" {
|
||
t.Errorf("Edges[0] = %s→%s,期望 n1→n2", first.Edges[0].From, first.Edges[0].To)
|
||
}
|
||
if first.Edges[1].From != "n2" || first.Edges[1].To != "n3" {
|
||
t.Errorf("Edges[1] = %s→%s,期望 n2→n3", first.Edges[1].From, first.Edges[1].To)
|
||
}
|
||
}
|
||
|
||
// TestAppendExplainChildConcurrentResultsHaveUniqueIDs 并发构建多份 ExplainResult,
|
||
// 断言每份内部的节点 ID 都不重复——这是原全局计数器方案会失败的场景。
|
||
func TestAppendExplainChildConcurrentResultsHaveUniqueIDs(t *testing.T) {
|
||
const parsers = 8
|
||
const nodesPerParser = 60
|
||
|
||
results := make([]connection.ExplainResult, parsers)
|
||
var wg sync.WaitGroup
|
||
wg.Add(parsers)
|
||
for i := 0; i < parsers; i++ {
|
||
go func(idx int) {
|
||
defer wg.Done()
|
||
parentID := appendExplainChild(&results[idx], "", connection.ExplainNode{OpDetail: "root"})
|
||
for n := 1; n < nodesPerParser; n++ {
|
||
parentID = appendExplainChild(&results[idx], parentID, connection.ExplainNode{
|
||
OpDetail: fmt.Sprintf("node-%d", n),
|
||
})
|
||
}
|
||
}(i)
|
||
}
|
||
wg.Wait()
|
||
|
||
for idx := range results {
|
||
seen := make(map[string]int, nodesPerParser)
|
||
for _, node := range results[idx].Nodes {
|
||
seen[node.ID]++
|
||
if seen[node.ID] > 1 {
|
||
t.Fatalf("result[%d] 出现重复节点 ID %s(并发解析互相踩踏编号)", idx, node.ID)
|
||
}
|
||
}
|
||
if len(results[idx].Nodes) != nodesPerParser {
|
||
t.Errorf("result[%d] 节点数 = %d,期望 %d", idx, len(results[idx].Nodes), nodesPerParser)
|
||
}
|
||
// 每个 Edge 的两端都必须存在于本 result 的节点集合内。
|
||
for _, edge := range results[idx].Edges {
|
||
if _, ok := seen[edge.From]; !ok {
|
||
t.Errorf("result[%d] Edge.From=%s 不存在于本 result 节点集合", idx, edge.From)
|
||
}
|
||
if _, ok := seen[edge.To]; !ok {
|
||
t.Errorf("result[%d] Edge.To=%s 不存在于本 result 节点集合", idx, edge.To)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestAppendExplainChildKeepsPresetNodeID 保证显式指定的 ID 不被覆盖。
|
||
func TestAppendExplainChildKeepsPresetNodeID(t *testing.T) {
|
||
var result connection.ExplainResult
|
||
if got := appendExplainChild(&result, "", connection.ExplainNode{ID: "custom", OpDetail: "root"}); got != "custom" {
|
||
t.Fatalf("预设 ID 被覆盖为 %s", got)
|
||
}
|
||
}
|