同步完整源码至远程:扁平化重构现状 + 未入库源文件

- 补入库:server/internal/db/dump.go、redis 分页/扫描预算测试、opsxdb-mcp main_test、
  packages/opsxdb-mcp package-lock、docs 审计与对比文档等 37 个新文件
- 含结构扁平化进行中的 80 处修改与 155 处旧结构删除(server/ src/ src-tauri/ 为当前有效源)
- 构建产物(bin/target/dist)与签名私钥仍按规则排除
This commit is contained in:
logan committed 2026-10-16 03:47:33 +08:00
1 parent 5c0be49217
commit 2c0bfafd8d
269 files changed
+10372 -16435

No files matched your search

+52
View File
@@ -1,12 +1,18 @@
package ai
import (
"context"
"encoding/json"
"io"
"net/http"
"strings"
"testing"
)
type roundTripFunc func(*http.Request) (*http.Response, error)
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) }
func TestValidateEndpoint(t *testing.T) {
if err := validateEndpoint("https://api.deepseek.com/v1", false); err != nil {
t.Fatalf("https 应直接放行: %v", err)
@@ -98,3 +104,49 @@ func TestBuildMessagesCoversActions(t *testing.T) {
t.Fatal("user 消息应包含 SQL")
}
}
func TestAskChatsWithoutClaimingDatabaseExecution(t *testing.T) {
request := ChatReq{Action: "ask", Prompt: "这个库里有哪些表?", Driver: "mysql", Database: "demo"}
msgs := BuildMessages(request)
if !strings.Contains(msgs[0].Content, "demo(mysql)") || !strings.Contains(msgs[0].Content, "不要声称已经查询") {
t.Fatalf("Ask should carry context without pretending to query: %s", msgs[0].Content)
}
if strings.Contains(msgs[0].Content, "只输出可执行 SQL") {
t.Fatal("Ask must not force a SQL response")
}
generate := BuildMessages(ChatReq{Action: "generate", Prompt: "查 users", Driver: "mysql", Database: "demo"})
if !strings.Contains(generate[0].Content, "生成一条 mysql 方言的 SQL") {
t.Fatalf("SQL generation lost its dedicated instruction: %s", generate[0].Content)
}
offline := chatStub(request)
if offline.SQL != "" || !strings.Contains(offline.Content, "离线体验无法回答") {
t.Fatalf("offline Ask must not pretend to generate or query: %+v", offline)
}
}
func TestAskRemoteRequestIsChatAndReturnsNoUnrequestedSQL(t *testing.T) {
p := NewRemoteProvider(Config{Provider: "deepseek", Protocol: ProtoChat, BaseURL: "https://api.example.test/v1", APIKey: "test-secret", Model: "test-model"})
p.hc = &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
if req.URL.Path != "/v1/chat/completions" {
t.Fatalf("unexpected endpoint: %s", req.URL)
}
if req.Header.Get("Authorization") != "Bearer test-secret" {
t.Fatal("missing auth")
}
var payload struct {
Messages []Message `json:"messages"`
Tools []any `json:"tools"`
}
if err := json.NewDecoder(req.Body).Decode(&payload); err != nil {
t.Fatal(err)
}
if len(payload.Messages) != 2 || len(payload.Tools) != 0 || !strings.Contains(payload.Messages[0].Content, "demo(mysql)") || strings.Contains(payload.Messages[0].Content, "只输出可执行 SQL") {
t.Fatalf("Ask should be contextual chat without tools: %+v", payload)
}
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader(`{"choices":[{"message":{"content":"当前表结构上下文不足,无法确认表名。"}}]}`)), Header: make(http.Header)}, nil
})}
res, err := p.Complete(context.Background(), ChatReq{Action: "ask", Prompt: "有哪些表?", Driver: "mysql", Database: "demo"})
if err != nil || res.SQL != "" || !strings.Contains(res.Content, "无法确认表名") {
t.Fatalf("Ask must return a plain answer without invented SQL: %+v err=%v", res, err)
}
}