v2.10.1 43db1bb

service_test.go RawHistoryFolder

1package health
2
3import (
4	"context"
5	"errors"
6	"testing"
7
8	"github.com/bnema/zerowrap"
9	"github.com/stretchr/testify/assert"
10	"github.com/stretchr/testify/mock"
11
12	"github.com/bnema/gordon/internal/boundaries/in/mocks"
13	"github.com/bnema/gordon/internal/domain"
14)
15
16func testLogger() zerowrap.Logger {
17	return zerowrap.Default()
18}
19
20func TestService_CheckRoute_ContainerNotFound(t *testing.T) {
21	configSvc := mocks.NewMockConfigService(t)
22	containerSvc := mocks.NewMockContainerService(t)
23	prober := mocks.NewMockHTTPProber(t)
24
25	containerSvc.EXPECT().Get(mock.Anything, "app.example.com").Return(nil, false)
26
27	svc := NewService(configSvc, containerSvc, prober, testLogger())
28	route := domain.Route{Domain: "app.example.com", Image: "myapp:latest"}
29
30	health := svc.CheckRoute(context.Background(), route)
31
32	assert.Equal(t, "app.example.com", health.Domain)
33	assert.Equal(t, "not found", health.ContainerStatus)
34	assert.Equal(t, 0, health.HTTPStatus)
35	assert.False(t, health.Healthy)
36	assert.Equal(t, "container not found", health.Error)
37}
38
39func TestService_CheckRoute_ContainerNotRunning(t *testing.T) {
40	configSvc := mocks.NewMockConfigService(t)
41	containerSvc := mocks.NewMockContainerService(t)
42	prober := mocks.NewMockHTTPProber(t)
43
44	container := &domain.Container{
45		ID:     "abc123",
46		Status: "stopped",
47	}
48	containerSvc.EXPECT().Get(mock.Anything, "app.example.com").Return(container, true)
49
50	svc := NewService(configSvc, containerSvc, prober, testLogger())
51	route := domain.Route{Domain: "app.example.com", Image: "myapp:latest"}
52
53	health := svc.CheckRoute(context.Background(), route)
54
55	assert.Equal(t, "app.example.com", health.Domain)
56	assert.Equal(t, "stopped", health.ContainerStatus)
57	assert.Equal(t, 0, health.HTTPStatus)
58	assert.False(t, health.Healthy)
59	assert.Contains(t, health.Error, "container is stopped")
60}
61
62func TestService_CheckRoute_HTTPProbeSuccess(t *testing.T) {
63	configSvc := mocks.NewMockConfigService(t)
64	containerSvc := mocks.NewMockContainerService(t)
65	prober := mocks.NewMockHTTPProber(t)
66
67	container := &domain.Container{
68		ID:     "abc123",
69		Status: "running",
70	}
71	containerSvc.EXPECT().Get(mock.Anything, "app.example.com").Return(container, true)
72	prober.EXPECT().Probe(mock.Anything, "https://app.example.com/").Return(200, int64(45), nil)
73
74	svc := NewService(configSvc, containerSvc, prober, testLogger())
75	route := domain.Route{Domain: "app.example.com", Image: "myapp:latest"}
76
77	health := svc.CheckRoute(context.Background(), route)
78
79	assert.Equal(t, "app.example.com", health.Domain)
80	assert.Equal(t, "running", health.ContainerStatus)
81	assert.Equal(t, 200, health.HTTPStatus)
82	assert.Equal(t, int64(45), health.ResponseTimeMs)
83	assert.True(t, health.Healthy)
84	assert.Empty(t, health.Error)
85}
86
87func TestService_CheckRoute_HTTPProbeFailure(t *testing.T) {
88	configSvc := mocks.NewMockConfigService(t)
89	containerSvc := mocks.NewMockContainerService(t)
90	prober := mocks.NewMockHTTPProber(t)
91
92	container := &domain.Container{
93		ID:     "abc123",
94		Status: "running",
95	}
96	containerSvc.EXPECT().Get(mock.Anything, "app.example.com").Return(container, true)
97	prober.EXPECT().Probe(mock.Anything, "https://app.example.com/").Return(0, int64(0), errors.New("connection refused"))
98
99	svc := NewService(configSvc, containerSvc, prober, testLogger())
100	route := domain.Route{Domain: "app.example.com", Image: "myapp:latest"}
101
102	health := svc.CheckRoute(context.Background(), route)
103
104	assert.Equal(t, "app.example.com", health.Domain)
105	assert.Equal(t, "running", health.ContainerStatus)
106	assert.Equal(t, 0, health.HTTPStatus)
107	assert.False(t, health.Healthy)
108	assert.Equal(t, "connection refused", health.Error)
109}
110
111func TestService_CheckRoute_HTTPStatus5xx(t *testing.T) {
112	configSvc := mocks.NewMockConfigService(t)
113	containerSvc := mocks.NewMockContainerService(t)
114	prober := mocks.NewMockHTTPProber(t)
115
116	container := &domain.Container{
117		ID:     "abc123",
118		Status: "running",
119	}
120	containerSvc.EXPECT().Get(mock.Anything, "app.example.com").Return(container, true)
121	prober.EXPECT().Probe(mock.Anything, "https://app.example.com/").Return(502, int64(100), nil)
122
123	svc := NewService(configSvc, containerSvc, prober, testLogger())
124	route := domain.Route{Domain: "app.example.com", Image: "myapp:latest"}
125
126	health := svc.CheckRoute(context.Background(), route)
127
128	assert.Equal(t, "app.example.com", health.Domain)
129	assert.Equal(t, "running", health.ContainerStatus)
130	assert.Equal(t, 502, health.HTTPStatus)
131	assert.Equal(t, int64(100), health.ResponseTimeMs)
132	assert.False(t, health.Healthy) // 5xx is not healthy
133	assert.Empty(t, health.Error)
134}
135
136func TestService_CheckAllRoutes(t *testing.T) {
137	configSvc := mocks.NewMockConfigService(t)
138	containerSvc := mocks.NewMockContainerService(t)
139	prober := mocks.NewMockHTTPProber(t)
140
141	routes := []domain.Route{
142		{Domain: "app1.example.com", Image: "app1:latest"},
143		{Domain: "app2.example.com", Image: "app2:latest"},
144	}
145	configSvc.EXPECT().GetRoutes(mock.Anything).Return(routes)
146
147	container1 := &domain.Container{ID: "abc", Status: "running"}
148	container2 := &domain.Container{ID: "def", Status: "stopped"}
149	containerSvc.EXPECT().Get(mock.Anything, "app1.example.com").Return(container1, true)
150	containerSvc.EXPECT().Get(mock.Anything, "app2.example.com").Return(container2, true)
151
152	// Only running containers get probed
153	prober.EXPECT().Probe(mock.Anything, "https://app1.example.com/").Return(200, int64(30), nil)
154
155	svc := NewService(configSvc, containerSvc, prober, testLogger())
156
157	results := svc.CheckAllRoutes(context.Background())
158
159	assert.Len(t, results, 2)
160
161	assert.NotNil(t, results["app1.example.com"])
162	assert.Equal(t, "running", results["app1.example.com"].ContainerStatus)
163	assert.Equal(t, 200, results["app1.example.com"].HTTPStatus)
164	assert.True(t, results["app1.example.com"].Healthy)
165
166	assert.NotNil(t, results["app2.example.com"])
167	assert.Equal(t, "stopped", results["app2.example.com"].ContainerStatus)
168	assert.Equal(t, 0, results["app2.example.com"].HTTPStatus)
169	assert.False(t, results["app2.example.com"].Healthy)
170}
171
172func TestService_CheckAllRoutes_NoRoutes(t *testing.T) {
173	configSvc := mocks.NewMockConfigService(t)
174	containerSvc := mocks.NewMockContainerService(t)
175	prober := mocks.NewMockHTTPProber(t)
176
177	configSvc.EXPECT().GetRoutes(mock.Anything).Return([]domain.Route{})
178
179	svc := NewService(configSvc, containerSvc, prober, testLogger())
180
181	results := svc.CheckAllRoutes(context.Background())
182
183	assert.Empty(t, results)
184}

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