Fix profile updates and validate configuration before apply

This commit is contained in:
ms2k 2026-10-05 21:45:05 +09:00
commit 2bf14948d7

View file

@ -2,7 +2,10 @@ package budget
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
@ -46,6 +49,98 @@ func TestApply(t *testing.T) {
}
}
func TestApplyShared(t *testing.T) {
c := testConfig()
c.Shared["editor"] = "compute"
f := newFake()
f.units = []unit{{Name: "[email protected]", Active: "active"}}
a := application{c, homePaths(t.TempDir()), f, func(...string) error { return nil }, io.Discard}
if err := a.apply(); err != nil {
t.Fatal(err)
}
if got := values(f.sets[f.units[0].Name])["MemoryMax"].Value(); got != uint64(17179869184) {
t.Fatalf("shared app ceiling: got %v, want 16 GiB", got)
}
data, err := os.ReadFile(filepath.Join(a.paths.units, "[email protected]/50-budget.conf"))
if err != nil || !strings.Contains(string(data), "MemoryMax=16G\n") {
t.Fatalf("persistent shared ceiling: %q, %v", data, err)
}
}
func TestApplyScopes(t *testing.T) {
c := testConfig()
c.Profiles["compute"] = profile{"400%", "2G", "4G", "1G"}
c.Profiles["bulk"] = profile{"200%", "1G", "2G", "1G"}
c.Profiles["cache"] = profile{"100%", "1G", "3G", "0"}
f := newFake()
f.units = []unit{
{Name: "budget-c123abc.scope", Description: "compute workload", Active: "active"},
{Name: "budget-b123abc.scope", Description: "bulk workload", Active: "active"},
{Name: "budget-c456abc.scope", Description: "cache workload", Active: "active"},
{Name: "budget-s123abc.scope", Description: "search workload", Active: "active"},
{Name: "budget-c123def.scope", Description: "compute workload", Active: "inactive"},
}
a := application{c, homePaths(t.TempDir()), f, func(...string) error { return nil }, io.Discard}
if err := a.apply(); err != nil {
t.Fatal(err)
}
for name, want := range map[string]uint64{
"budget-c123abc.scope": 4294967296, "budget-b123abc.scope": 2147483648,
"budget-c456abc.scope": 3221225472,
} {
if got := values(f.sets[name])["MemoryMax"].Value(); got != want {
t.Errorf("%s ceiling: got %v, want %d", name, got, want)
}
}
if len(f.sets) != 3 {
t.Fatalf("unexpected scope updates: %v", keys(f.sets))
}
}
func TestApplyInvalid(t *testing.T) {
for name, change := range map[string]func(*config){
"resource": func(c *config) {
p := c.Profiles["default"]
p.High = "invalid"
c.Profiles["default"] = p
},
"command": func(c *config) { c.Commands["java"] = "missing" },
"prefix": func(c *config) { c.Prefixes["default"] = []string{"snap-firefox"} },
} {
for _, active := range []bool{false, true} {
t.Run(fmt.Sprintf("%s/active=%t", name, active), func(t *testing.T) {
c := testConfig()
change(&c)
f := newFake()
if active {
f.units = []unit{{Name: "app-browser-123.scope", Active: "active"}}
}
var events []string
f.events = &events
p := homePaths(t.TempDir())
put(t, filepath.Join(p.units, "user.conf"), "keep")
a := application{c, p, f, func(args ...string) error {
events = append(events, strings.Join(args, " "))
return nil
}, io.Discard}
if err := a.apply(); err == nil {
t.Fatal("invalid configuration accepted")
}
if len(events) != 0 {
t.Fatal("invalid configuration changed units:", events)
}
entries, err := os.ReadDir(p.units)
if err != nil || len(entries) != 1 || entries[0].Name() != "user.conf" {
t.Fatalf("invalid configuration changed files: %v, %v", entries, err)
}
if _, err := os.Stat(p.state); !os.IsNotExist(err) {
t.Fatalf("invalid configuration created state: %v", err)
}
})
}
}
}
func TestSharedOverride(t *testing.T) {
f := newFake()
f.units = []unit{