v3.3.1 31f1eec

parse_visibility_test.go RawHistoryFolder

1package appmanifest_test
2
3import (
4	"testing"
5
6	"github.com/stretchr/testify/assert"
7	"github.com/stretchr/testify/require"
8
9	"github.com/bnema/gordon/internal/adapters/in/appmanifest"
10	"github.com/bnema/gordon/internal/domain"
11)
12
13// TestParse_VisibilityNormalization proves absent visibility becomes
14// explicit public, an internal interface keeps its TLS absent, and a
15// public interface still defaults an absent TLS to auto.
16func TestParse_VisibilityNormalization(t *testing.T) {
17	t.Run("absent visibility stays public with auto tls", func(t *testing.T) {
18		doc := `
19name = "blog"
20[services.web]
21image = "registry.example.com/blog/web:1.0.0"
22[[services.web.http]]
23host = "blog.example.com"
24port = 8080
25`
26		spec, _, err := appmanifest.Parse([]byte(doc), "blog.toml")
27		require.NoError(t, err)
28		require.Len(t, spec.Services[0].HTTP, 1)
29		iface := spec.Services[0].HTTP[0]
30		assert.Equal(t, domain.AppVisibilityPublic, iface.Visibility)
31		assert.Equal(t, domain.AppTLSAuto, iface.TLS)
32	})
33
34	t.Run("explicit internal keeps tls absent and has no host", func(t *testing.T) {
35		doc := `
36name = "blog"
37[services.api]
38image = "registry.example.com/blog/api:1.0.0"
39[services.api.readiness]
40type = "http"
41path = "/healthz"
42[[services.api.http]]
43visibility = "internal"
44port = 8080
45`
46		spec, _, err := appmanifest.Parse([]byte(doc), "blog.toml")
47		require.NoError(t, err)
48		require.Len(t, spec.Services[0].HTTP, 1)
49		iface := spec.Services[0].HTTP[0]
50		assert.Equal(t, domain.AppVisibilityInternal, iface.Visibility)
51		assert.Empty(t, iface.Host)
52		assert.Empty(t, iface.TLS, "an internal interface must not inherit the public auto tls default")
53		assert.True(t, spec.Services[0].InternallyOnlyPort(8080))
54	})
55
56	t.Run("explicit public normalizes to auto tls", func(t *testing.T) {
57		doc := `
58name = "blog"
59[services.web]
60image = "registry.example.com/blog/web:1.0.0"
61[[services.web.http]]
62host = "blog.example.com"
63port = 8080
64visibility = "public"
65`
66		spec, _, err := appmanifest.Parse([]byte(doc), "blog.toml")
67		require.NoError(t, err)
68		assert.Equal(t, domain.AppVisibilityPublic, spec.Services[0].HTTP[0].Visibility)
69		assert.Equal(t, domain.AppTLSAuto, spec.Services[0].HTTP[0].TLS)
70	})
71}
72
73// TestParse_VisibilityRejections proves invalid combinations fail at
74// validation with the sentinel error.
75func TestParse_VisibilityRejections(t *testing.T) {
76	tests := map[string]string{
77		"internal forbids host": `
78name = "blog"
79[services.api]
80image = "registry.example.com/blog/api:1.0.0"
81[[services.api.http]]
82host = "blog.example.com"
83port = 8080
84visibility = "internal"
85`,
86		"internal forbids tls": `
87name = "blog"
88[services.api]
89image = "registry.example.com/blog/api:1.0.0"
90[[services.api.http]]
91port = 8080
92visibility = "internal"
93tls = "auto"
94`,
95		"internal forbids explicitly empty tls": `
96name = "blog"
97[services.api]
98image = "registry.example.com/blog/api:1.0.0"
99[[services.api.http]]
100port = 8080
101visibility = "internal"
102tls = ""
103`,
104		"unknown visibility value": `
105name = "blog"
106[services.web]
107image = "registry.example.com/blog/web:1.0.0"
108[[services.web.http]]
109host = "blog.example.com"
110port = 8080
111visibility = "vpn"
112`,
113		"internal port also published as tcp": `
114name = "blog"
115[services.api]
116image = "registry.example.com/blog/api:1.0.0"
117[[services.api.http]]
118port = 8080
119visibility = "internal"
120[[services.api.tcp]]
121port = 8080
122`,
123	}
124	for name, doc := range tests {
125		t.Run(name, func(t *testing.T) {
126			_, _, err := appmanifest.Parse([]byte(doc), "blog.toml")
127			require.Error(t, err)
128			assert.ErrorIs(t, err, domain.ErrInvalidAppSpec)
129		})
130	}
131}
132
133// TestParse_VisibilityUnknownFieldStillRejected proves the parser stays
134// strict: a near-miss key is still a decode error.
135func TestParse_VisibilityUnknownFieldStillRejected(t *testing.T) {
136	doc := `
137name = "blog"
138[services.web]
139image = "registry.example.com/blog/web:1.0.0"
140[[services.web.http]]
141host = "blog.example.com"
142port = 8080
143visibilities = "internal"
144`
145	_, _, err := appmanifest.Parse([]byte(doc), "blog.toml")
146	require.Error(t, err)
147	assert.ErrorIs(t, err, domain.ErrInvalidAppSpec)
148}

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