Files
Maven/internal/store/tools_test.go
T
kami 6b80fd0c0f tools: add scope column for capability model
Add a 'scope' TEXT column (default 'homelab') to the tools table so tools
can be namespaced by scope (e.g. "homelab:restart", "datacenter:reboot").
Backward-compat: bare name defaults to "homelab" scope.

Changes:
- Migration #1: ALTER TABLE tools ADD COLUMN scope
- store.Tool: add Scope field, update all SQL and scanTool()
- ipc.Tool DTO and request types: add Scope field
- CoreAPI interface: pass scope in ProposeTool/EnableTool
- storeAPI adapters: forward scope
- cmd/mavend/voice: pass scope (empty → homelab)
- cmd/mavweb/tools: show scope column in UI tables, hidden fields
- All tests updated for scope field
- Migration test made dynamic (startVer = len(migrations))
2026-07-05 11:40:15 +04:00

63 lines
1.9 KiB
Go

package store
import (
"context"
"testing"
"time"
)
// TestToolLifecycle covers propose → enable → disable, the states the authed
// /tools page drives. Disable must revert an enabled tool to 'proposed' (kept
// in the store, won't run) and be idempotent.
func TestToolLifecycle(t *testing.T) {
s := newTestStore(t)
ctx := context.Background()
now := time.Now()
// Propose with empty scope → defaults to "homelab".
if _, err := s.ProposeTool(ctx, "restart_svc", "restart the service", "", now); err != nil {
t.Fatalf("propose: %v", err)
}
if err := s.EnableTool(ctx, "restart_svc", []string{"systemctl", "restart", "x"}, true, "", now); err != nil {
t.Fatalf("enable: %v", err)
}
tl, _ := s.LookupTool(ctx, "restart_svc")
if tl.Status != "enabled" {
t.Fatalf("after enable: status=%q want enabled", tl.Status)
}
if tl.Scope != "homelab" {
t.Fatalf("after enable: scope=%q want homelab", tl.Scope)
}
// Propose with explicit scope.
if _, err := s.ProposeTool(ctx, "reboot", "reboot the server", "datacenter", now); err != nil {
t.Fatalf("propose with scope: %v", err)
}
if err := s.EnableTool(ctx, "reboot", []string{"reboot"}, true, "datacenter", now); err != nil {
t.Fatalf("enable with scope: %v", err)
}
tl2, _ := s.LookupTool(ctx, "reboot")
if tl2.Scope != "datacenter" {
t.Fatalf("explicit scope: %q want datacenter", tl2.Scope)
}
if err := s.DisableTool(ctx, "restart_svc"); err != nil {
t.Fatalf("disable: %v", err)
}
tl, err := s.LookupTool(ctx, "restart_svc")
if err != nil {
t.Fatalf("lookup after disable: %v", err)
}
if tl.Status != "proposed" {
t.Fatalf("after disable: status=%q want proposed", tl.Status)
}
// idempotent: disabling an already-proposed (or absent) tool is a no-op.
if err := s.DisableTool(ctx, "restart_svc"); err != nil {
t.Fatalf("disable idempotent: %v", err)
}
if err := s.DisableTool(ctx, "does_not_exist"); err != nil {
t.Fatalf("disable absent must be no-op: %v", err)
}
}