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:
72
platform/internal/dbsync/lww_audit_test.go
Normal file
72
platform/internal/dbsync/lww_audit_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package dbsync
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLwwOverrideStoreAndPurge(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
st, err := NewFileStore(filepath.Join(dir, "dbsync"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
old := LwwOverride{
|
||||
TenantID: 1,
|
||||
ChannelID: "ch1",
|
||||
Table: "orders",
|
||||
RowPK: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
|
||||
Entry: EntryAgentPush,
|
||||
Policy: string(PolicyLWWSource),
|
||||
Outcome: OutcomeApplied,
|
||||
CreatedAt: time.Now().UTC().Add(-100 * 24 * time.Hour),
|
||||
}
|
||||
fresh := LwwOverride{
|
||||
TenantID: 1,
|
||||
ChannelID: "ch1",
|
||||
Table: "orders",
|
||||
RowPK: "bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee",
|
||||
Entry: EntryDrain,
|
||||
Policy: string(PolicyLWWSource),
|
||||
Outcome: OutcomeApplied,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
}
|
||||
if err := st.AddLwwOverride(old); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := st.AddLwwOverride(fresh); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
list, err := st.ListLwwOverrides(1, "ch1", 10)
|
||||
if err != nil || len(list) != 2 {
|
||||
t.Fatalf("list=%d err=%v", len(list), err)
|
||||
}
|
||||
n, err := st.PurgeLwwOverridesBefore(time.Now().UTC().Add(-90 * 24 * time.Hour))
|
||||
if err != nil || n != 1 {
|
||||
t.Fatalf("purge n=%d err=%v", n, err)
|
||||
}
|
||||
list, err = st.ListLwwOverrides(0, "", 10)
|
||||
if err != nil || len(list) != 1 {
|
||||
t.Fatalf("after purge list=%d err=%v", len(list), err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanReconcile(t *testing.T) {
|
||||
ok, _ := CanReconcile(nil, time.Minute)
|
||||
if !ok {
|
||||
t.Fatal("nil channel should allow")
|
||||
}
|
||||
past := time.Now().UTC().Add(-10 * time.Minute)
|
||||
ch := &Channel{LastReconcileAt: &past}
|
||||
ok, _ = CanReconcile(ch, 5*time.Minute)
|
||||
if !ok {
|
||||
t.Fatal("expected allow")
|
||||
}
|
||||
recent := time.Now().UTC()
|
||||
ch.LastReconcileAt = &recent
|
||||
ok, wait := CanReconcile(ch, 5*time.Minute)
|
||||
if ok || wait <= 0 {
|
||||
t.Fatalf("expected deny wait>0 ok=%v wait=%v", ok, wait)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user