1package app
2
3import (
4 "strings"
5 "testing"
6
7 "github.com/spf13/viper"
8 "github.com/stretchr/testify/assert"
9 "github.com/stretchr/testify/require"
10
11 "github.com/bnema/gordon/internal/domain"
12)
13
14func TestBuildAppDevicePolicies_ValidConfigUnmarshal(t *testing.T) {
15 v := viper.New()
16 v.SetConfigType("toml")
17 require.NoError(t, v.ReadConfig(strings.NewReader(`
18[app_devices.test_gpu]
19cdi = ["example.com/gpu=GPU-test-uuid"]
20allowed_apps = ["demo"]
21allowed_services = ["worker"]
22`)))
23
24 var cfg Config
25 require.NoError(t, v.Unmarshal(&cfg))
26 require.NoError(t, validateAppDeviceKeys(v.GetStringMap("app_devices")))
27
28 policies, err := buildAppDevicePolicies(cfg)
29 require.NoError(t, err)
30 require.Contains(t, policies, "test_gpu")
31 policy := policies["test_gpu"]
32 assert.Equal(t, "test_gpu", policy.Name)
33 assert.Equal(t, []string{"example.com/gpu=GPU-test-uuid"}, policy.CDI)
34 assert.Equal(t, []string{"demo"}, policy.AllowedApps)
35 assert.Equal(t, []string{"worker"}, policy.AllowedServices)
36}
37
38func TestBuildAppDevicePolicies_Invalid(t *testing.T) {
39 cases := []struct {
40 name string
41 device AppDevicePolicy
42 reason string
43 }{
44 {"empty cdi", AppDevicePolicy{AllowedApps: []string{"demo"}, AllowedServices: []string{"worker"}}, "cdi must not be empty"},
45 {"raw path", AppDevicePolicy{CDI: []string{"/dev/card0"}, AllowedApps: []string{"demo"}, AllowedServices: []string{"worker"}}, "not a host path"},
46 {"aggregate", AppDevicePolicy{CDI: []string{"example.com/gpu=all"}, AllowedApps: []string{"demo"}, AllowedServices: []string{"worker"}}, "not allowed"},
47 {"empty allowed apps", AppDevicePolicy{CDI: []string{"example.com/gpu=0"}, AllowedServices: []string{"worker"}}, "allowed apps must not be empty"},
48 {"empty allowed services", AppDevicePolicy{CDI: []string{"example.com/gpu=0"}, AllowedApps: []string{"demo"}}, "allowed services must not be empty"},
49 {"duplicate cdi", AppDevicePolicy{CDI: []string{"example.com/gpu=0", "example.com/gpu=0"}, AllowedApps: []string{"demo"}, AllowedServices: []string{"worker"}}, "duplicate cdi device"},
50 }
51 for _, tc := range cases {
52 t.Run(tc.name, func(t *testing.T) {
53 cfg := Config{AppDevices: map[string]AppDevicePolicy{"test_gpu": tc.device}}
54 _, err := buildAppDevicePolicies(cfg)
55 require.Error(t, err)
56 assert.Contains(t, err.Error(), "app_devices.test_gpu")
57 assert.Contains(t, err.Error(), tc.reason, "the field-level reason must survive redaction")
58 assert.ErrorIs(t, err, domain.ErrDevicePolicy)
59 for _, id := range tc.device.CDI {
60 assert.NotContains(t, err.Error(), id, "config errors must not echo CDI IDs")
61 }
62 })
63 }
64}
65
66func TestValidateAppDeviceKeys_UnknownKey(t *testing.T) {
67 v := viper.New()
68 v.SetConfigType("toml")
69 require.NoError(t, v.ReadConfig(strings.NewReader(`
70[app_devices.test_gpu]
71cdis = ["example.com/gpu=0"]
72allowed_apps = ["demo"]
73allowed_services = ["worker"]
74`)))
75
76 err := validateAppDeviceKeys(v.GetStringMap("app_devices"))
77 require.Error(t, err)
78 assert.Contains(t, err.Error(), "unknown key")
79 assert.Contains(t, err.Error(), "app_devices.test_gpu")
80}
81
82func TestValidateAppDeviceKeys_Valid(t *testing.T) {
83 v := viper.New()
84 v.SetConfigType("toml")
85 require.NoError(t, v.ReadConfig(strings.NewReader(`
86[app_devices.test_gpu]
87cdi = ["example.com/gpu=0"]
88allowed_apps = ["demo"]
89allowed_services = ["worker"]
90`)))
91
92 require.NoError(t, validateAppDeviceKeys(v.GetStringMap("app_devices")))
93 require.NoError(t, validateAppDeviceKeys(nil))
94}
95
96func TestBuildAppDevicePolicies_CopiesSlices(t *testing.T) {
97 cfg := Config{AppDevices: map[string]AppDevicePolicy{
98 "test_gpu": {
99 CDI: []string{"example.com/gpu=0"},
100 AllowedApps: []string{"demo"},
101 AllowedServices: []string{"worker"},
102 },
103 }}
104 policies, err := buildAppDevicePolicies(cfg)
105 require.NoError(t, err)
106 // Mutating the published policy must not affect later builds from the
107 // same config: reloads always rebuild from Config.
108 policies["test_gpu"].CDI[0] = "mutated"
109 again, err := buildAppDevicePolicies(cfg)
110 require.NoError(t, err)
111 assert.Equal(t, []string{"example.com/gpu=0"}, again["test_gpu"].CDI)
112}
113
114func TestConfig_AppDevicesDefaultsEmpty(t *testing.T) {
115 policies, err := buildAppDevicePolicies(Config{})
116 require.NoError(t, err)
117 assert.Empty(t, policies)
118}