v2.31.0-dev18 7b9923f

tls_test.go RawHistoryFolder

1package cli
2
3import (
4	"bytes"
5	"context"
6	"encoding/json"
7	"testing"
8	"time"
9
10	"github.com/stretchr/testify/assert"
11	"github.com/stretchr/testify/require"
12
13	"github.com/bnema/gordon/internal/adapters/dto"
14	climocks "github.com/bnema/gordon/internal/adapters/in/cli/mocks"
15)
16
17func TestTLSStatus_HumanOutput(t *testing.T) {
18	notAfter := time.Date(2026, 5, 29, 12, 0, 0, 0, time.UTC)
19
20	cpMock := climocks.NewMockControlPlane(t)
21	cpMock.EXPECT().GetTLSStatus(context.Background()).Return(&dto.TLSStatusResponse{
22		ACMEEnabled:     true,
23		ConfiguredMode:  "acme",
24		EffectiveMode:   "acme-staging",
25		SelectionReason: "configured",
26		TokenSource:     "env",
27		Certificates: []dto.TLSCertificateEntry{
28			{
29				ID:        "cert-abc123",
30				Names:     []string{"example.com", "www.example.com"},
31				Status:    "valid",
32				NotAfter:  notAfter,
33				LastError: "",
34			},
35		},
36		Routes: []dto.TLSRouteCoverage{
37			{
38				Domain:       "example.com",
39				Covered:      true,
40				CoveredBy:    "cert-abc123",
41				RequiredACME: true,
42			},
43			{
44				Domain:       "internal.local",
45				Covered:      false,
46				RequiredACME: false,
47				Error:        "self-signed cert",
48			},
49		},
50		Errors: []string{"route internal.local has no ACME cert"},
51	}, nil).Once()
52
53	var buf bytes.Buffer
54	err := runTLSStatusCmd(context.Background(), cpMock, &buf, false)
55	require.NoError(t, err)
56
57	output := buf.String()
58	t.Log("Human output:\n", output)
59
60	// Check mode info is present
61	assert.Contains(t, output, "ACME:")
62	assert.Contains(t, output, "enabled")
63	assert.Contains(t, output, "Configured Mode:")
64	assert.Contains(t, output, "acme")
65	assert.Contains(t, output, "Effective Mode:")
66	assert.Contains(t, output, "acme-staging")
67
68	// Check token source (not value)
69	assert.Contains(t, output, "Token Source:")
70	assert.Contains(t, output, "env")
71	assert.NotContains(t, output, "token_secret_value")
72
73	// Check certificate ID
74	assert.Contains(t, output, "cert-abc123")
75	assert.Contains(t, output, "example.com, www.example.com")
76	assert.Contains(t, output, "valid")
77	assert.Contains(t, output, notAfter.Format(time.DateTime))
78
79	// Check route coverage
80	assert.Contains(t, output, "example.com")
81	assert.Contains(t, output, "covered=yes")
82	assert.Contains(t, output, "covered_by=cert-abc123")
83	assert.Contains(t, output, "internal.local")
84	assert.Contains(t, output, "covered=no")
85
86	// Check errors section
87	assert.Contains(t, output, "Errors")
88	assert.Contains(t, output, "route internal.local has no ACME cert")
89}
90
91func TestTLSStatus_JSONOutput(t *testing.T) {
92	notAfter := time.Date(2026, 5, 29, 12, 0, 0, 0, time.UTC)
93
94	cpMock := climocks.NewMockControlPlane(t)
95	cpMock.EXPECT().GetTLSStatus(context.Background()).Return(&dto.TLSStatusResponse{
96		ACMEEnabled:    true,
97		ConfiguredMode: "acme",
98		EffectiveMode:  "acme-staging",
99		TokenSource:    "env",
100		Certificates: []dto.TLSCertificateEntry{
101			{
102				ID:        "cert-abc123",
103				Names:     []string{"example.com", "www.example.com"},
104				Status:    "valid",
105				NotAfter:  notAfter,
106				LastError: "",
107			},
108		},
109	}, nil).Once()
110
111	var buf bytes.Buffer
112	err := runTLSStatusCmd(context.Background(), cpMock, &buf, true)
113	require.NoError(t, err)
114
115	var result dto.TLSStatusResponse
116	err = json.Unmarshal(buf.Bytes(), &result)
117	require.NoError(t, err)
118
119	assert.True(t, result.ACMEEnabled)
120	assert.Equal(t, "acme", result.ConfiguredMode)
121	assert.Equal(t, "acme-staging", result.EffectiveMode)
122	assert.Equal(t, "env", result.TokenSource)
123	require.Len(t, result.Certificates, 1)
124	assert.Equal(t, "cert-abc123", result.Certificates[0].ID)
125	assert.Equal(t, []string{"example.com", "www.example.com"}, result.Certificates[0].Names)
126	assert.Equal(t, "valid", result.Certificates[0].Status)
127	assert.Equal(t, notAfter.Format(time.RFC3339), result.Certificates[0].NotAfter.Format(time.RFC3339))
128}

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