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 "gordon/internal/boundaries/in/mocks"
13 "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}