Files
Agent-System-Backend/internal/settings/personal_patch_test.go
weichen f965c5877f Enforce settings ownership and versioned shared configuration
Why: Personal project rows and credentials could override system policy and split shared business configuration by actor.

Evidence: SELF receipts /tmp/project-settings-backend-full-20261014-973d52c1.json and /tmp/rdm-settings-backend-final-green-20261014-03.json preserve full commands, object snapshots and output. PostgreSQL and current-head CI remain NOT_RUN.

Impact: Add scope-aware APIs, a fail-closed optional project configuration authorizer, CAS updates and explicit atomic migration. Preserve legacy rows and private preferences; protected secrets and runtime model routes obey system ownership.

Refs: P1/P2/P8, docs/records/2026-10-14-rdm-settings-completion-contract.md
2026-10-14 02:28:41 +00:00

56 lines
2.5 KiB
Go

package settings
import (
"agent-system-backend/internal/store"
core "agent-system-core"
"path/filepath"
"testing"
)
func TestPersonalMergePatchNullDeletesNonsecretLeafAndFollowsDefaults(t *testing.T) {
st, err := store.OpenSQLite(filepath.Join(t.TempDir(), "personal.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { st.Close() })
s := NewService(st)
s.SetExtraSchema([]core.SettingDesc{{Key: "plugins.example.personalNumber", Type: core.SettingNumber, Default: 3}})
if err := s.MergeUser("alice", map[string]any{"ui.theme": "light", "ui.cardflow": false, "retrieval.topK": 12, "plugins.example.personalNumber": 9}); err != nil {
t.Fatal(err)
}
if err := s.MergeUser("alice", map[string]any{"ui.theme": nil, "ui.cardflow": nil, "retrieval.topK": nil, "plugins.example.personalNumber": nil}); err != nil {
t.Fatalf("valid personal reset rejected: %v", err)
}
raw, err := st.GetSettingsScope("alice", ScopeUser)
if err != nil {
t.Fatal(err)
}
for _, path := range []string{"ui.theme", "ui.cardflow", "retrieval.topK", "plugins.example.personalNumber"} {
if _, exists := valueAtPath(raw, path); exists {
t.Fatalf("null persisted instead of deleting %s", path)
}
}
eff := s.Effective("alice", "")
if eff.GetString("ui.theme", "") != "dark" || !eff.GetBool("ui.cardflow", false) || eff.GetNumber("retrieval.topK", 0) != 5 {
t.Fatalf("personal defaults not restored: theme=%q cardflow=%v topK=%v", eff.GetString("ui.theme", ""), eff.GetBool("ui.cardflow", false), eff.GetNumber("retrieval.topK", 0))
}
s.SetExtraSchema([]core.SettingDesc{{Key: "plugins.example.personalNumber", Type: core.SettingNumber, Default: 7}})
if got := s.Effective("alice", "").GetNumber("plugins.example.personalNumber", 0); got != 7 {
t.Fatal("null reset copied old default instead of inheriting future default")
}
if err := s.MergePersonalProject("alice", "p", map[string]any{"crossSession.read": true}); err != nil {
t.Fatal(err)
}
if err := s.MergePersonalProject("alice", "p", map[string]any{"crossSession.read": nil}); err != nil {
t.Fatal(err)
}
if s.Effective("alice", "p").GetBool("crossSession.read", true) {
t.Fatal("personal project boolean reset not effective")
}
for _, values := range []map[string]any{{"ui": nil}, {"unknown": nil}, {"models.chat.apiKey": nil}, {"_policy.managedModels": nil}, {"deliverables.style": nil}} {
if err := s.MergeUser("alice", values); err == nil {
t.Fatal("unknown/ancestor/managed/private/wrong scope reset accepted")
}
}
}