app_devices_test.go RawHistoryFolder

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}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close