main bac21c6

app_mounts_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 TestBuildAppMountPolicies_ValidConfigUnmarshal(t *testing.T) {
15	v := viper.New()
16	v.SetConfigType("toml")
17	require.NoError(t, v.ReadConfig(strings.NewReader(`
18[app_mounts.config]
19source = "/srv/gordon/binds/blog/config"
20read_only = true
21allowed_apps = ["blog", "docs"]
22allowed_services = ["web"]
23root = "/srv/gordon"
24`)))
25
26	var cfg Config
27	require.NoError(t, v.Unmarshal(&cfg))
28
29	policies, err := buildAppMountPolicies(cfg)
30	require.NoError(t, err)
31	require.Contains(t, policies, "config")
32	policy := policies["config"]
33	assert.Equal(t, "config", policy.Name)
34	assert.Equal(t, "/srv/gordon/binds/blog/config", policy.Source)
35	assert.True(t, policy.ReadOnly)
36	assert.Equal(t, []string{"blog", "docs"}, policy.AllowedApps)
37	assert.Equal(t, []string{"web"}, policy.AllowedServices)
38	assert.Equal(t, "/srv/gordon", policy.Root)
39}
40
41func TestBuildAppMountPolicies_Invalid(t *testing.T) {
42	cases := []struct {
43		name   string
44		mount  AppMountPolicy
45		reason string
46	}{
47		{"empty source", AppMountPolicy{AllowedApps: []string{"blog"}, AllowedServices: []string{"web"}}, "source must not be empty"},
48		{"relative source", AppMountPolicy{Source: "binds", AllowedApps: []string{"blog"}, AllowedServices: []string{"web"}}, "source must be absolute"},
49		{"empty allowed apps", AppMountPolicy{Source: "/srv/binds", AllowedServices: []string{"web"}}, "allowed apps must not be empty"},
50		{"empty allowed services", AppMountPolicy{Source: "/srv/binds", AllowedApps: []string{"blog"}}, "allowed services must not be empty"},
51		{"unclean root", AppMountPolicy{Source: "/srv/binds", AllowedApps: []string{"blog"}, AllowedServices: []string{"web"}, Root: "/srv/../etc"}, "root must be a normalized clean path"},
52	}
53	for _, tc := range cases {
54		t.Run(tc.name, func(t *testing.T) {
55			cfg := Config{AppMounts: map[string]AppMountPolicy{"config": tc.mount}}
56			_, err := buildAppMountPolicies(cfg)
57			require.Error(t, err)
58			assert.Contains(t, err.Error(), "app_mounts.config")
59			assert.Contains(t, err.Error(), tc.reason, "the field-level reason must survive redaction")
60			assert.ErrorIs(t, err, domain.ErrBindPolicy)
61			if tc.mount.Source != "" {
62				assert.NotContains(t, err.Error(), tc.mount.Source, "config errors must not echo the source path")
63			}
64			if tc.mount.Root != "" {
65				assert.NotContains(t, err.Error(), tc.mount.Root, "config errors must not echo the root path")
66			}
67		})
68	}
69}
70
71func TestRedactedBindPolicyReason_RedactsConfiguredValues(t *testing.T) {
72	_, err := buildAppMountPolicies(Config{AppMounts: map[string]AppMountPolicy{
73		"config": {
74			Source:          "/srv/binds/blog/config",
75			AllowedApps:     []string{"Blog"},
76			AllowedServices: []string{"web"},
77		},
78	}})
79
80	require.Error(t, err)
81	assert.Contains(t, err.Error(), "app name must be a DNS label")
82	assert.NotContains(t, err.Error(), "/srv/binds/blog/config", "configured paths must never be echoed")
83	assert.NotContains(t, err.Error(), "Blog", "configured names must never be echoed")
84}
85
86func TestBuildAppMountPolicies_DefaultRootIsSourceParent(t *testing.T) {
87	cfg := Config{AppMounts: map[string]AppMountPolicy{
88		"config": {
89			Source:          "/srv/binds/config",
90			AllowedApps:     []string{"blog"},
91			AllowedServices: []string{"web"},
92		},
93	}}
94	policies, err := buildAppMountPolicies(cfg)
95	require.NoError(t, err)
96	assert.Equal(t, "/srv/binds", policies["config"].Root)
97}
98
99func TestConfig_AppMountsDefaultsEmpty(t *testing.T) {
100	policies, err := buildAppMountPolicies(Config{})
101	require.NoError(t, err)
102	assert.Empty(t, policies)
103}

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