public_tls_test.go RawHistoryFolder

1package domain
2
3import (
4	"strings"
5	"testing"
6	"time"
7
8	"github.com/stretchr/testify/assert"
9	"github.com/stretchr/testify/require"
10)
11
12func TestIsValidHTTP01Token(t *testing.T) {
13	tests := []struct {
14		name  string
15		token string
16		want  bool
17	}{
18		{name: "valid", token: "abc123_-token", want: true},
19		{name: "empty", token: "", want: false},
20		{name: "slash", token: "abc/def", want: false},
21		{name: "backslash", token: `abc\def`, want: false},
22		{name: "dot dot", token: "abc..def", want: false},
23		{name: "nul", token: "abc\x00def", want: false},
24		{name: "too long", token: strings.Repeat("a", MaxHTTP01TokenLength+1), want: false},
25	}
26
27	for _, tt := range tests {
28		t.Run(tt.name, func(t *testing.T) {
29			assert.Equal(t, tt.want, IsValidHTTP01Token(tt.token))
30		})
31	}
32}
33
34func TestACMEChallengeModeParse(t *testing.T) {
35	tests := []struct {
36		input string
37		want  ACMEChallengeMode
38		ok    bool
39	}{
40		{"", ACMEChallengeAuto, true},
41		{"auto", ACMEChallengeAuto, true},
42		{"http-01", ACMEChallengeHTTP01, true},
43		{"cloudflare-dns-01", ACMEChallengeCloudflareDNS01, true},
44		{"dns", "", false},
45	}
46
47	for _, tt := range tests {
48		got, err := ParseACMEChallengeMode(tt.input)
49		if tt.ok {
50			require.NoError(t, err)
51			assert.Equal(t, tt.want, got)
52		} else {
53			require.Error(t, err)
54			assert.ErrorIs(t, err, ErrACMEChallengeInvalid)
55		}
56	}
57}
58
59func TestManagedCertificateCovers(t *testing.T) {
60	apexCert := ManagedCertificate{Names: []string{"example.com", "*.example.com"}}
61	prodCert := ManagedCertificate{Names: []string{"prod.example.com", "*.prod.example.com"}}
62
63	assert.True(t, apexCert.Covers("example.com"))
64	assert.True(t, apexCert.Covers("app.example.com"))
65	assert.False(t, apexCert.Covers("api.prod.example.com"), "*.example.com must not cover two labels")
66	assert.True(t, prodCert.Covers("api.prod.example.com"))
67	assert.False(t, apexCert.Covers("other.test"))
68}
69
70func TestManagedCertificateHealth(t *testing.T) {
71	// Fixed reference time for deterministic tests.
72	now := time.Date(2026, 4, 29, 12, 0, 0, 0, time.UTC)
73
74	tests := []struct {
75		name string
76		cert ManagedCertificate
77		want TLSCertificateStatus
78	}{
79		{
80			name: "valid certificate far from expiry",
81			cert: ManagedCertificate{NotAfter: now.Add(60 * 24 * time.Hour)},
82			want: TLSCertificateStatusValid,
83		},
84		{
85			name: "expired certificate",
86			cert: ManagedCertificate{NotAfter: now.Add(-time.Hour)},
87			want: TLSCertificateStatusExpired,
88		},
89		{
90			name: "warning when less than 30 days remain",
91			cert: ManagedCertificate{NotAfter: now.Add(15 * 24 * time.Hour)},
92			want: TLSCertificateStatusWarning,
93		},
94		{
95			name: "warning exactly at 30 day boundary",
96			cert: ManagedCertificate{NotAfter: now.Add(30 * 24 * time.Hour)},
97			want: TLSCertificateStatusWarning,
98		},
99		{
100			name: "missing when NotAfter is zero",
101			cert: ManagedCertificate{},
102			want: TLSCertificateStatusMissing,
103		},
104		{
105			name: "error when LastError is set",
106			cert: ManagedCertificate{
107				NotAfter:  now.Add(60 * 24 * time.Hour),
108				LastError: "issuer unavailable",
109			},
110			want: TLSCertificateStatusError,
111		},
112		{
113			name: "error takes priority over expired",
114			cert: ManagedCertificate{
115				NotAfter:  now.Add(-time.Hour),
116				LastError: "something went wrong",
117			},
118			want: TLSCertificateStatusError,
119		},
120		{
121			name: "error takes priority over zero NotAfter",
122			cert: ManagedCertificate{
123				LastError: "something went wrong",
124			},
125			want: TLSCertificateStatusError,
126		},
127	}
128
129	for _, tt := range tests {
130		t.Run(tt.name, func(t *testing.T) {
131			got := tt.cert.Health(now)
132			assert.Equal(t, tt.want, got)
133		})
134	}
135}

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