feat: ship loose-offline dbsync (validate, agent push, LWW audit)

Add UUID/FK channel checks, agent whitelist/push APIs, bindings, super-admin LWW audit with rollback, reconcile rate limits, and sync docs. Default customers stay opt-in; company conflict UI is removed.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
whm
2026-07-31 17:54:14 +08:00
parent 632057c857
commit 76cdcd760e
39 changed files with 3302 additions and 199 deletions

View File

@@ -0,0 +1,65 @@
package dbsync
import (
"context"
"path/filepath"
"testing"
)
func TestIsTextLikePK(t *testing.T) {
good := []string{"TEXT", "VARCHAR(36)", "uuid", "CHARACTER VARYING", "NVARCHAR(64)", "char(36)"}
for _, g := range good {
if !isTextLikePK(g) {
t.Fatalf("expected text-like: %s", g)
}
}
bad := []string{"INTEGER", "INT", "BIGINT", "SERIAL", "BIGSERIAL", "int(11)", "NUMERIC", "DECIMAL(10,2)"}
for _, b := range bad {
if isTextLikePK(b) {
t.Fatalf("expected reject: %s", b)
}
}
}
func TestValidateChannelConfigEmpty(t *testing.T) {
ch := &Channel{}
if err := ValidateChannelConfig(ch); err == nil {
t.Fatal("expected error for empty tables")
}
ch.Local.Tables = []string{"orders"}
if err := ValidateChannelConfig(ch); err != nil {
t.Fatal(err)
}
}
func TestValidateChannelAgainstDBRejectsIntegerPK(t *testing.T) {
dir := t.TempDir()
dsn := "file:" + filepath.ToSlash(filepath.Join(dir, "t.db")) + "?_pragma=foreign_keys(1)"
db, err := Open(DriverSQLite, dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
if _, err = db.Exec(`CREATE TABLE orders (id INTEGER PRIMARY KEY, title TEXT)`); err != nil {
t.Fatal(err)
}
if _, err = db.Exec(`CREATE TABLE orders_uuid (id TEXT PRIMARY KEY, title TEXT)`); err != nil {
t.Fatal(err)
}
bad := &Channel{
Local: Endpoint{Driver: DriverSQLite, DSN: dsn, Tables: []string{"orders"}},
Remote: Endpoint{Driver: DriverSQLite, DSN: dsn, Tables: []string{"orders"}},
}
if err := ValidateChannelAgainstDB(context.Background(), bad); err == nil {
t.Fatal("expected reject INTEGER PK")
}
good := &Channel{
Local: Endpoint{Driver: DriverSQLite, DSN: dsn, Tables: []string{"orders_uuid"}},
Remote: Endpoint{Driver: DriverSQLite, DSN: dsn, Tables: []string{"orders_uuid"}},
}
if err := ValidateChannelAgainstDB(context.Background(), good); err != nil {
t.Fatal(err)
}
}