* feat(canvas): 参考素材 - 增加移除按钮并删除对应连线 * feat(canvas): 完善节点面板交互与拖拽反馈 * fix(canvas): 画布交互与小地图 - 统一浮层层级并完善节点识别 * feat(prompt-optimizer): 增加 AI 提示词优化器及宿主接入 * feat(canvas): 画布 - 增加自动整理节点按钮并补充文档 * chore(web): 前端 - 修正提示词优化器格式 * chore(backend): 后端 - 修正 Go 格式检查 * feat(workspace): 完善诊断导出与本地排障支持 * chore(web): 格式化诊断导出文件 * fix(web-test): 兼容 Bun 测试运行时 * fix(web-test): 对齐模型目录协议断言
105 lines
4.0 KiB
Go
105 lines
4.0 KiB
Go
package service
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"infinite-canvas/backend/internal/model"
|
|
"infinite-canvas/backend/internal/repository"
|
|
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func TestExportDiagnosticBundleRedactsSensitiveValues(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open("file:diagnostics-test?mode=memory&cache=shared"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.AutoMigrate(&model.Task{}, &model.TaskLog{}, &model.ApiCallLog{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
now := time.Now().UTC()
|
|
if err := db.Create(&model.Task{
|
|
ID: "task-1", UserID: "user-1", TraceID: "trace-1", RequestID: "req-1", ProjectID: "project-1", Type: "video_generate",
|
|
Status: model.TaskStatusFailed, Error: "authorization: task-secret", Prompt: "secret prompt", CreatedAt: now.Add(-time.Minute), UpdatedAt: now,
|
|
}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Create(&model.TaskLog{ID: "task-log-1", UserID: "user-1", TaskID: "task-1", TraceID: "trace-1", Message: "任务失败", Payload: "apiKey=log-secret", CreatedAt: now}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Create(&model.ApiCallLog{ID: "api-log-1", UserID: "user-1", TraceID: "trace-1", RequestID: "req-1", TaskID: "task-1", Path: "/v1/generations", RequestBody: "apiKey=body-secret", ResponseBody: "response-secret", UpstreamURL: "https://provider.example/result?signature=secret", Error: "token=api-secret", CreatedAt: now}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
svc := &Service{repo: repository.New(db)}
|
|
bundle, err := svc.ExportDiagnosticBundle("user-1", DiagnosticExportRequest{
|
|
From: now.Add(-2 * time.Minute).Format(time.RFC3339Nano), To: now.Add(time.Minute).Format(time.RFC3339Nano), TaskID: "task-1",
|
|
Description: "页面报错", ClientEvents: []DiagnosticClientEvent{{ID: "event-1", Message: "cookie=client-secret", TraceID: "trace-1"}},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
files := readDiagnosticTestZIP(t, bundle.Data)
|
|
manifest := files["manifest.json"]
|
|
if !strings.Contains(manifest, `"bundleId": "`+bundle.BundleID+`"`) {
|
|
t.Fatalf("manifest = %s", manifest)
|
|
}
|
|
all := strings.Join([]string{manifest, files["client/events.jsonl"], files["backend/tasks.jsonl"], files["backend/task-logs.jsonl"], files["backend/upstream-calls.jsonl"]}, "\n")
|
|
for _, secret := range []string{"task-secret", "secret prompt", "log-secret", "body-secret", "response-secret", "api-secret", "signature=secret", "client-secret"} {
|
|
if strings.Contains(all, secret) {
|
|
t.Fatalf("diagnostic bundle leaked %q: %s", secret, all)
|
|
}
|
|
}
|
|
if strings.Contains(files["backend/upstream-calls.jsonl"], "requestBody") || strings.Contains(files["backend/upstream-calls.jsonl"], "result?signature") {
|
|
t.Fatalf("upstream call contains excluded payload or query: %s", files["backend/upstream-calls.jsonl"])
|
|
}
|
|
}
|
|
|
|
func TestExportDiagnosticBundleRejectsForeignTask(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open("file:diagnostics-ownership-test?mode=memory&cache=shared"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.AutoMigrate(&model.Task{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Create(&model.Task{ID: "foreign-task", UserID: "user-2", CreatedAt: time.Now(), UpdatedAt: time.Now()}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = (&Service{repo: repository.New(db)}).ExportDiagnosticBundle("user-1", DiagnosticExportRequest{TaskID: "foreign-task"})
|
|
if err == nil || !strings.Contains(err.Error(), "任务不存在或无权访问") {
|
|
t.Fatalf("foreign task error = %v", err)
|
|
}
|
|
}
|
|
|
|
func readDiagnosticTestZIP(t *testing.T, data []byte) map[string]string {
|
|
t.Helper()
|
|
archive, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
files := make(map[string]string, len(archive.File))
|
|
for _, file := range archive.File {
|
|
reader, err := file.Open()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
content, readErr := io.ReadAll(reader)
|
|
closeErr := reader.Close()
|
|
if readErr != nil {
|
|
t.Fatal(readErr)
|
|
}
|
|
if closeErr != nil {
|
|
t.Fatal(closeErr)
|
|
}
|
|
files[file.Name] = string(content)
|
|
}
|
|
return files
|
|
}
|