package app
import (
"testing"
"GoNavi-Wails/internal/connection"
)
// SQL Server SHOWPLAN_XML fixture:单 Clustered Index Scan + Predicate + RunTime。
const sqlServerShowPlanXMLSingleScan = `
`
func TestParseSQLServerExplain_ClusteredIndexScan(t *testing.T) {
result, err := parseSQLServerExplain("SELECT * FROM users WHERE age > 18", sqlServerShowPlanXMLSingleScan, connection.ExplainFormatXML)
if err != nil {
t.Fatalf("解析失败:%v", err)
}
if len(result.Nodes) != 1 {
t.Fatalf("应有 1 个 RelOp 节点,got=%d", len(result.Nodes))
}
node := result.Nodes[0]
if node.OpType != connection.ExplainOpScan {
t.Fatalf("Clustered Index Scan 应为 SCAN,got=%s", node.OpType)
}
if node.Table != "users" {
t.Fatalf("Table got=%s want=users", node.Table)
}
if node.Index != "PK_users" {
t.Fatalf("Index got=%s want=PK_users", node.Index)
}
if node.EstRows != 10000 {
t.Fatalf("EstRows got=%d want=10000", node.EstRows)
}
if node.ActualRows != 10000 {
t.Fatalf("ActualRows got=%d want=10000", node.ActualRows)
}
if node.DurationMs != 5 {
t.Fatalf("DurationMs got=%v want=5", node.DurationMs)
}
if !containsFlag(node.Flags, connection.ExplainFlagFullScan) {
t.Fatalf("Clustered Scan 应有 FULL_SCAN flag")
}
}
// SQL Server fixture:Nested Loops JOIN + 两个子节点。
const sqlServerShowPlanXMLNestedLoops = `
`
func TestParseSQLServerExplain_NestedLoopsRecursesChildren(t *testing.T) {
result, err := parseSQLServerExplain("SELECT * FROM orders o JOIN users u ON o.user_id = u.id", sqlServerShowPlanXMLNestedLoops, connection.ExplainFormatXML)
if err != nil {
t.Fatalf("解析失败:%v", err)
}
if len(result.Nodes) != 3 {
t.Fatalf("应有 3 个节点(Nested Loops + 2 子),got=%d", len(result.Nodes))
}
joinNode := result.Nodes[0]
if joinNode.OpType != connection.ExplainOpJoin {
t.Fatalf("顶层应为 JOIN,got=%s", joinNode.OpType)
}
// 两个子节点(通过 Edges 验证)
childCount := 0
for _, e := range result.Edges {
if e.From == joinNode.ID {
childCount++
}
}
if childCount != 2 {
t.Fatalf("JOIN 应有 2 个直接子节点,got=%d", childCount)
}
// 找到 Index Seek 子节点
var indexSeek *connection.ExplainNode
for i := range result.Nodes {
if result.Nodes[i].OpType == connection.ExplainOpIndexScan {
indexSeek = &result.Nodes[i]
}
}
if indexSeek == nil {
t.Fatal("应有一个 Index Seek 节点")
}
if indexSeek.Index != "IX_users_id" {
t.Fatalf("Index Seek 应使用 IX_users_id,got=%s", indexSeek.Index)
}
}
func TestParseSQLServerExplain_InvalidXMLReturnsWarning(t *testing.T) {
result, err := parseSQLServerExplain("SELECT 1", "