WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s

This commit is contained in:
yawan
2026-09-22 16:19:06 +08:00
commit f7fc992f57
3929 changed files with 1006607 additions and 0 deletions
@@ -0,0 +1,157 @@
package workbench_test
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
apperrors "github.com/Tencent/WeKnora/internal/errors"
"github.com/Tencent/WeKnora/internal/middleware"
"github.com/Tencent/WeKnora/internal/types"
"github.com/Tencent/WeKnora/internal/workbench"
"github.com/gin-gonic/gin"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
func operationTestDB(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open(filepath.Join(t.TempDir(), "operations.db")), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)})
if err != nil {
t.Fatal(err)
}
sqlDB, err := db.DB()
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = sqlDB.Close() })
if err := db.AutoMigrate(&workbench.OperationLog{}); err != nil {
t.Fatal(err)
}
return db
}
func operationTestContext() context.Context {
ctx := context.WithValue(context.Background(), types.TenantIDContextKey, uint64(1))
ctx = context.WithValue(ctx, types.UserIDContextKey, "alice")
return context.WithValue(ctx, types.TenantRoleContextKey, types.TenantRoleAdmin)
}
func TestOperationAuditUsesRenderedErrorStatus(t *testing.T) {
gin.SetMode(gin.TestMode)
for _, tc := range []struct {
name, method, level string
err error
written, status int
rows int
}{
{"read forbidden", "GET", "warn", fmt.Errorf("wrapped: %w", apperrors.NewForbiddenError("无权访问")), 0, 403, 2},
{"write failed", "POST", "error", errors.New("private prompt and secret-api-key"), 0, 500, 2},
{"read failed", "GET", "error", errors.New("private prompt"), 0, 500, 2},
{"written response", "POST", "info", errors.New("late error"), 202, 202, 1},
{"successful read", "GET", "", nil, 200, 200, 0},
} {
t.Run(tc.name, func(t *testing.T) {
db := operationTestDB(t)
h := workbench.NewHandler(workbench.NewService(db))
router := gin.New()
router.Use(middleware.ErrorHandler(), h.OperationAudit())
router.Handle(tc.method, "/agents/:id", func(c *gin.Context) {
if tc.err != nil {
_ = c.Error(tc.err)
}
if tc.written > 0 {
c.JSON(tc.written, gin.H{"retained": true})
}
})
request := httptest.NewRequest(tc.method, "/agents/agent-1", nil).WithContext(operationTestContext())
response := httptest.NewRecorder()
router.ServeHTTP(response, request)
if response.Code != tc.status || !json.Valid(response.Body.Bytes()) {
t.Fatalf("invalid rendered response: %d %s", response.Code, response.Body.String())
}
var rows []workbench.OperationLog
if err := db.Find(&rows).Error; err != nil {
t.Fatal(err)
}
if len(rows) != tc.rows {
t.Fatalf("unexpected log count: %+v", rows)
}
for _, row := range rows {
if row.Status != tc.status || row.Level != tc.level || row.TenantID != 1 || row.OwnerID != "alice" || row.ResourceID != "agent-1" {
t.Fatalf("incorrect final status or ownership: %+v", row)
}
serialized, _ := json.Marshal(row)
if strings.Contains(string(serialized), "private prompt") || strings.Contains(string(serialized), "secret-api-key") {
t.Fatal("raw error leaked into operation log")
}
}
})
}
}
func TestSystemLogQueryHidesWarningsAndOtherTenants(t *testing.T) {
db := operationTestDB(t)
for _, row := range []workbench.OperationLog{
{ID: "error", TenantID: 1, Kind: "system", Level: "error"},
{ID: "warn", TenantID: 1, Kind: "system", Level: "warn"},
{ID: "warning", TenantID: 1, Kind: "system", Level: "warning"},
{ID: "other-tenant", TenantID: 2, Kind: "system", Level: "error"},
} {
if err := db.Create(&row).Error; err != nil {
t.Fatal(err)
}
}
router := gin.New()
router.GET("/operations/:recordKind", workbench.NewHandler(workbench.NewService(db)).OperationRecords)
request := httptest.NewRequest(http.MethodGet, "/operations/system", nil).WithContext(operationTestContext())
response := httptest.NewRecorder()
router.ServeHTTP(response, request)
var body struct {
Data []workbench.OperationLog `json:"data"`
Total int `json:"total"`
}
if err := json.Unmarshal(response.Body.Bytes(), &body); err != nil {
t.Fatal(err)
}
if response.Code != 200 || body.Total != 1 || len(body.Data) != 1 || body.Data[0].ID != "error" {
t.Fatalf("system log filtering failed: %s", response.Body.String())
}
}
func TestWorkbenchErrorRetainsResponseAndRecordsDiagnostic(t *testing.T) {
db := operationTestDB(t)
h := workbench.NewHandler(workbench.NewService(db))
router := gin.New()
router.Use(middleware.ErrorHandler(), h.OperationAudit())
router.GET("/workbench/:kind", h.List)
request := httptest.NewRequest(http.MethodGet, "/workbench/unknown-kind", nil).WithContext(operationTestContext())
response := httptest.NewRecorder()
router.ServeHTTP(response, request)
var body map[string]any
if err := json.Unmarshal(response.Body.Bytes(), &body); err != nil {
t.Fatalf("response was rendered more than once or is invalid: %s", response.Body.String())
}
if response.Code != http.StatusBadRequest || body["success"] != false || body["message"] != workbench.ErrInvalid.Error() || len(body) != 2 {
t.Fatalf("existing error response changed: %d %s", response.Code, response.Body.String())
}
var rows []workbench.OperationLog
if err := db.Find(&rows).Error; err != nil {
t.Fatal(err)
}
if len(rows) != 2 {
t.Fatalf("expected audit and system records, got %+v", rows)
}
for _, row := range rows {
if row.Status != http.StatusBadRequest || row.Metadata["error_class"] != "invalid" || row.Metadata["error_message"] != workbench.ErrInvalid.Error() || row.Level != "warn" {
t.Fatalf("underlying error was not attached to logs: %+v", row)
}
}
}