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
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:
@@ -0,0 +1,109 @@
|
||||
package workbench
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
// PostgreSQL distinguishes the conflict target from EXCLUDED; SQLite does not
|
||||
// reject an unqualified status predicate, so this must execute on PostgreSQL.
|
||||
func TestPostgresContentFeedbackAcceptanceUpsert(t *testing.T) {
|
||||
dsn := os.Getenv("WORKBENCH_TEST_POSTGRES_DSN")
|
||||
if dsn == "" {
|
||||
t.Skip("WORKBENCH_TEST_POSTGRES_DSN is not set")
|
||||
}
|
||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sqlDB.Close()
|
||||
schema := "wb_memory_test_" + strings.ReplaceAll(uuid.NewString(), "-", "")
|
||||
if err := db.Exec("CREATE SCHEMA " + schema).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
if err := db.Exec("DROP SCHEMA " + schema + " CASCADE").Error; err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
testDSN := dsn + " search_path=" + schema
|
||||
if strings.Contains(dsn, "://") {
|
||||
separator := "?"
|
||||
if strings.Contains(dsn, "?") {
|
||||
separator = "&"
|
||||
}
|
||||
testDSN = dsn + separator + "search_path=" + schema
|
||||
}
|
||||
testDB, err := gorm.Open(postgres.Open(testDSN), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testSQL, err := testDB.DB()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testSQL.Close()
|
||||
if err := testDB.AutoMigrate(&Resource{}, &Job{}, &Revision{}, &Event{}, &Consumption{}, &PendingMemory{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, ctx, resource, job := feedbackHarnessWithService(t, NewService(testDB))
|
||||
accept := func() PendingMemory {
|
||||
t.Helper()
|
||||
version := resource.Version
|
||||
if err := s.executeTask(ctx, job, resource, ActionRequest{SegmentID: "segment"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := s.save(ctx, resource, version); err != nil {
|
||||
t.Fatalf("acceptance must commit with its pending feedback: %v", err)
|
||||
}
|
||||
var persisted Resource
|
||||
if err := testDB.First(&persisted, "id = ?", resource.ID).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := decode[TaskData](persisted.Data)
|
||||
if err != nil || persisted.Version != version+1 || data.Segments[0].Status != "accepted" {
|
||||
t.Fatalf("acceptance was not persisted atomically: %+v %v", persisted, err)
|
||||
}
|
||||
var rows []PendingMemory
|
||||
if err := testDB.Where("tenant_id = ? AND owner_id = ?", resource.TenantID, resource.OwnerID).Find(&rows).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rows) != 1 || rows[0].Content != "开场先说结论,再给案例" {
|
||||
t.Fatalf("feedback lost or duplicated: %+v", rows)
|
||||
}
|
||||
return rows[0]
|
||||
}
|
||||
first := accept()
|
||||
if first.Status != "pending" {
|
||||
t.Fatalf("initial acceptance did not create pending feedback: %+v", first)
|
||||
}
|
||||
if repeated := accept(); repeated.ID != first.ID || !repeated.UpdatedAt.Equal(first.UpdatedAt) {
|
||||
t.Fatalf("repeat acceptance mutated an existing pending candidate: %+v", repeated)
|
||||
}
|
||||
for _, state := range []string{"invalidated", "accepted", "rejected"} {
|
||||
stamp := time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC)
|
||||
if err := testDB.Model(&PendingMemory{}).Where("id = ?", first.ID).
|
||||
Updates(map[string]any{"status": state, "updated_at": stamp}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
row := accept()
|
||||
if state == "invalidated" {
|
||||
if row.Status != "pending" || !row.UpdatedAt.After(stamp) {
|
||||
t.Fatalf("reacceptance did not restore invalidated feedback: %+v", row)
|
||||
}
|
||||
} else if row.Status != state || !row.UpdatedAt.Equal(stamp) {
|
||||
t.Fatalf("reacceptance changed explicit %s review: %+v", state, row)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user