Files
2026-06-24 17:29:45 +02:00

152 lines
4.2 KiB
Go

package config
import (
"os"
"path/filepath"
"strings"
"testing"
"nadir/internal/module"
"nadir/internal/rbac"
"github.com/danielgtaylor/huma/v2"
)
// fakeModule implements module.Module with a fixed permission set, so config
// tests don't depend on the concrete modules' exec behavior.
type fakeModule struct {
id string
perms []rbac.Permission
}
func (f fakeModule) ID() string { return f.id }
func (f fakeModule) Permissions() []rbac.Permission { return f.perms }
func (f fakeModule) Register(huma.API) {}
func mods() []module.Module {
return []module.Module{
fakeModule{id: "system", perms: []rbac.Permission{rbac.Read, rbac.Write, rbac.Root}},
fakeModule{id: "services", perms: []rbac.Permission{rbac.Read, rbac.Write}},
}
}
func TestSecureCookieDefaultsFalse(t *testing.T) {
if (&File{}).SecureCookie() {
t.Error("omitted secure_tls should default to false")
}
yes := true
if !(&File{Server: Server{SecureTLS: &yes}}).SecureCookie() {
t.Error("secure_tls: true should enable the Secure flag")
}
}
func TestLoad(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.yaml")
os.WriteFile(path, []byte(`
server:
secure_tls: false
roles:
admin:
"*": ["*"]
assignments:
urania: [admin]
`), 0600)
f, err := Load(path)
if err != nil {
t.Fatal(err)
}
if f.SecureCookie() {
t.Error("secure_tls: false not parsed")
}
if len(f.Roles["admin"]) == 0 || len(f.Assignments["urania"]) == 0 {
t.Error("roles/assignments not parsed")
}
}
func TestLoadMissingFile(t *testing.T) {
if _, err := Load(filepath.Join(t.TempDir(), "nope.yaml")); err == nil {
t.Fatal("expected error for missing file")
}
}
func TestApplyValid(t *testing.T) {
f := &File{
Roles: map[string]map[string][]string{
"admin": {"*": {"*"}},
"auditor": {"*": {"read"}},
"sysop": {"system": {"read", "root"}},
},
Assignments: map[string][]string{"urania": {"admin"}, "bob": {"sysop"}},
}
roles := rbac.New()
if err := Apply(f, roles, mods()); err != nil {
t.Fatal(err)
}
if !roles.Can("urania", "services", rbac.Write) {
t.Error("admin wildcard not applied")
}
if !roles.Can("bob", "system", rbac.Root) || roles.Can("bob", "system", rbac.Write) {
t.Error("sysop grants not applied correctly")
}
}
func TestApplyErrors(t *testing.T) {
tests := []struct {
name string
f *File
}{
{"unknown module", &File{Roles: map[string]map[string][]string{
"r": {"firewall": {"read"}}}}},
{"unknown perm on known module", &File{Roles: map[string]map[string][]string{
"r": {"system": {"banana"}}}}},
{"wildcard module with perm no module exports", &File{Roles: map[string]map[string][]string{
"r": {"*": {"banana"}}}}},
{"assignment to unknown role", &File{
Roles: map[string]map[string][]string{"r": {"system": {"read"}}},
Assignments: map[string][]string{"u": {"ghost"}}}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := Apply(tt.f, rbac.New(), mods()); err == nil {
t.Errorf("expected error for %s", tt.name)
}
})
}
}
func TestApplyWildcardPermAlwaysOK(t *testing.T) {
f := &File{Roles: map[string]map[string][]string{"r": {"system": {"*"}}}}
if err := Apply(f, rbac.New(), mods()); err != nil {
t.Fatalf("wildcard permission should validate: %v", err)
}
}
func TestDefaultPathAndExpandPath(t *testing.T) {
defaultPath, err := DefaultPath()
if err != nil {
t.Fatalf("DefaultPath failed: %v", err)
}
expectedSuffix := filepath.Join("nadir", "config.yaml")
if !filepath.IsAbs(defaultPath) || !strings.HasSuffix(defaultPath, expectedSuffix) {
t.Errorf("expected default path to end with %q and be absolute, got %q", expectedSuffix, defaultPath)
}
expanded, err := ExpandPath("~/foo/config.yaml")
if err != nil {
t.Fatalf("ExpandPath failed: %v", err)
}
if !filepath.IsAbs(expanded) || !strings.HasSuffix(expanded, filepath.Join("foo", "config.yaml")) {
t.Errorf("ExpandPath did not resolve ~/ correctly, got %q", expanded)
}
plain := "/etc/nadir/config.yaml"
expandedPlain, err := ExpandPath(plain)
if err != nil {
t.Fatalf("ExpandPath failed for plain path: %v", err)
}
if expandedPlain != plain {
t.Errorf("expected no-op for %q, got %q", plain, expandedPlain)
}
}