Files
LocalAI/core/application/addon_usersettings_test.go
T
Ettore Di Giacinto 7ce4507da1 feat(addons): keep per-person settings that an add-on declares
An add-on can declare userSettings in its manifest: a key, the allowed
values and a default. The host keeps each person's answer in the account
database every frontend shares (one file when accounts are off), checks
every write against the declaration, and never asks the add-on.

GET and PUT /api/addons/{id}/user-settings read and write them. A write
needs the person's own browser session, so no API key, model or tool can
change one. A setting may carry a role that the host reads for its own
rules; the host fixes the values and the default of a role so a manifest
cannot loosen it. Pages reach their settings through sdk.userSettings,
which is bound to the add-on that calls it.

Assisted-by: Claude:claude-sonnet-5-5 go node
2026-11-03 22:46:22 +00:00

58 lines
2.1 KiB
Go

// SPDX-License-Identifier: MIT
package application
import (
"context"
"testing"
"github.com/mudler/LocalAI/core/config"
"github.com/mudler/LocalAI/core/http/auth"
)
// The chats read the answer that the settings routes wrote. Without accounts
// there is one local owner: the routes write under it, so a chat of any name
// must read it.
func TestStudioUserSettingReadsWhatTheRoutesWrite(t *testing.T) {
a := &Application{applicationConfig: &config.ApplicationConfig{Context: context.Background(), DataPath: t.TempDir()}}
store, err := a.AddonUserSettingsStore()
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
if v, ok, err := a.studioUserSetting(ctx, "", "memory", "save-mode"); err != nil || ok || v != "" {
t.Fatalf("unset = %q, %v, %v", v, ok, err)
}
if err := store.Set(ctx, auth.AddonLocalOwnerID, "memory", "save-mode", "auto"); err != nil {
t.Fatal(err)
}
for _, user := range []string{"", "anonymous", "someone"} {
v, ok, err := a.studioUserSetting(ctx, user, "memory", "save-mode")
if err != nil || !ok || v != "auto" {
t.Fatalf("user %q reads %q, %v, %v", user, v, ok, err)
}
}
if _, ok, _ := a.studioUserSetting(ctx, "", "memory", "other-key"); ok {
t.Fatal("another key must not read the value")
}
if _, ok, _ := a.studioUserSetting(ctx, "", "other-addon", "save-mode"); ok {
t.Fatal("another add-on must not read the value")
}
}
func TestAddonUserSettingsStoreSurvivesARestart(t *testing.T) {
dir := t.TempDir()
first := &Application{applicationConfig: &config.ApplicationConfig{Context: context.Background(), DataPath: dir}}
s, err := first.AddonUserSettingsStore()
if err != nil {
t.Fatal(err)
}
if err := s.Set(context.Background(), auth.AddonLocalOwnerID, "memory", "save-mode", "never"); err != nil {
t.Fatal(err)
}
second := &Application{applicationConfig: &config.ApplicationConfig{Context: context.Background(), DataPath: dir}}
v, ok, err := second.studioUserSetting(context.Background(), "", "memory", "save-mode")
if err != nil || !ok || v != "never" {
t.Fatalf("after a restart = %q, %v, %v", v, ok, err)
}
}