detached 2439aa3

refactor(admin): replace NewHandler positional params with HandlerDeps struct

Replaces the 11-positional-parameter NewHandler signature with a HandlerDeps
struct, folds in WithVolumeService, and updates all call sites (run.go and
~60 test invocations) to use the new struct form with a newTestHandler helper.
commit
2439aa323bacafea27fd9ef8a0c63d2038e43fae
author
brice · · 2026-03-19 05:47 UTC
parent
13bd0f0
signature
unsigned
4 files · +466 -117
M internal/adapters/in/http/admin/bootstrap_test.go +43 -7
@@ -24,7 +24,13 @@ func TestBootstrap_FullWorkflow_HappyPath(t *testing.T) {
2424 secretSvc := inmocks.NewMockSecretService(t)
2525 registrySvc := inmocks.NewMockRegistryService(t)
2626
27- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)
27+ handler := newTestHandler(t, func(d *HandlerDeps) {
28+ d.ConfigSvc = configSvc
29+ d.AuthSvc = authSvc
30+ d.ContainerSvc = containerSvc
31+ d.SecretSvc = secretSvc
32+ d.RegistrySvc = registrySvc
33+ })
2834
2935 payload := dto.BootstrapRequest{
3036 Domain: "app.example.com",
@@ -73,7 +79,12 @@ func TestBootstrap_Idempotent_Rerun(t *testing.T) {
7379 containerSvc := inmocks.NewMockContainerService(t)
7480 secretSvc := inmocks.NewMockSecretService(t)
7581
76- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
82+ handler := newTestHandler(t, func(d *HandlerDeps) {
83+ d.ConfigSvc = configSvc
84+ d.AuthSvc = authSvc
85+ d.ContainerSvc = containerSvc
86+ d.SecretSvc = secretSvc
87+ })
7788
7889 payload := dto.BootstrapRequest{
7990 Domain: "app.example.com",
@@ -137,7 +148,12 @@ func TestBootstrap_MissingDomain(t *testing.T) {
137148 containerSvc := inmocks.NewMockContainerService(t)
138149 secretSvc := inmocks.NewMockSecretService(t)
139150
140- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
151+ handler := newTestHandler(t, func(d *HandlerDeps) {
152+ d.ConfigSvc = configSvc
153+ d.AuthSvc = authSvc
154+ d.ContainerSvc = containerSvc
155+ d.SecretSvc = secretSvc
156+ })
141157
142158 body, err := json.Marshal(dto.BootstrapRequest{Image: "myapp:latest"})
143159 require.NoError(t, err)
@@ -158,7 +174,12 @@ func TestBootstrap_MissingImage(t *testing.T) {
158174 containerSvc := inmocks.NewMockContainerService(t)
159175 secretSvc := inmocks.NewMockSecretService(t)
160176
161- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
177+ handler := newTestHandler(t, func(d *HandlerDeps) {
178+ d.ConfigSvc = configSvc
179+ d.AuthSvc = authSvc
180+ d.ContainerSvc = containerSvc
181+ d.SecretSvc = secretSvc
182+ })
162183
163184 body, err := json.Marshal(dto.BootstrapRequest{Domain: "app.example.com"})
164185 require.NoError(t, err)
@@ -179,7 +200,12 @@ func TestBootstrap_NormalizesImageBeforeStoring(t *testing.T) {
179200 containerSvc := inmocks.NewMockContainerService(t)
180201 secretSvc := inmocks.NewMockSecretService(t)
181202
182- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
203+ handler := newTestHandler(t, func(d *HandlerDeps) {
204+ d.ConfigSvc = configSvc
205+ d.AuthSvc = authSvc
206+ d.ContainerSvc = containerSvc
207+ d.SecretSvc = secretSvc
208+ })
183209
184210 payload := dto.BootstrapRequest{Domain: "app.example.com", Image: "pitlane:v1"}
185211 configSvc.EXPECT().GetRegistryDomain().Return("reg.bnema.dev").Once()
@@ -225,7 +251,12 @@ func TestBootstrap_AddRouteValidationErrors(t *testing.T) {
225251 containerSvc := inmocks.NewMockContainerService(t)
226252 secretSvc := inmocks.NewMockSecretService(t)
227253
228- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
254+ handler := newTestHandler(t, func(d *HandlerDeps) {
255+ d.ConfigSvc = configSvc
256+ d.AuthSvc = authSvc
257+ d.ContainerSvc = containerSvc
258+ d.SecretSvc = secretSvc
259+ })
229260
230261 payload := dto.BootstrapRequest{Domain: "app.example.com", Image: "myapp:latest"}
231262 configSvc.EXPECT().GetRegistryDomain().Return("reg.bnema.dev").Once()
@@ -252,7 +283,12 @@ func TestBootstrap_PartialFailure_AttachmentError(t *testing.T) {
252283 containerSvc := inmocks.NewMockContainerService(t)
253284 secretSvc := inmocks.NewMockSecretService(t)
254285
255- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
286+ handler := newTestHandler(t, func(d *HandlerDeps) {
287+ d.ConfigSvc = configSvc
288+ d.AuthSvc = authSvc
289+ d.ContainerSvc = containerSvc
290+ d.SecretSvc = secretSvc
291+ })
256292
257293 payload := dto.BootstrapRequest{
258294 Domain: "app.example.com",
@@ -24,7 +24,13 @@ func TestBootstrap_FullWorkflow_HappyPath(t *testing.T) {
24 secretSvc := inmocks.NewMockSecretService(t)24 secretSvc := inmocks.NewMockSecretService(t)
25 registrySvc := inmocks.NewMockRegistryService(t)25 registrySvc := inmocks.NewMockRegistryService(t)
26 26
27- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)27+ handler := newTestHandler(t, func(d *HandlerDeps) {
28+ d.ConfigSvc = configSvc
29+ d.AuthSvc = authSvc
30+ d.ContainerSvc = containerSvc
31+ d.SecretSvc = secretSvc
32+ d.RegistrySvc = registrySvc
33+ })
28 34
29 payload := dto.BootstrapRequest{35 payload := dto.BootstrapRequest{
30 Domain: "app.example.com",36 Domain: "app.example.com",
@@ -73,7 +79,12 @@ func TestBootstrap_Idempotent_Rerun(t *testing.T) {
73 containerSvc := inmocks.NewMockContainerService(t)79 containerSvc := inmocks.NewMockContainerService(t)
74 secretSvc := inmocks.NewMockSecretService(t)80 secretSvc := inmocks.NewMockSecretService(t)
75 81
76- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)82+ handler := newTestHandler(t, func(d *HandlerDeps) {
83+ d.ConfigSvc = configSvc
84+ d.AuthSvc = authSvc
85+ d.ContainerSvc = containerSvc
86+ d.SecretSvc = secretSvc
87+ })
77 88
78 payload := dto.BootstrapRequest{89 payload := dto.BootstrapRequest{
79 Domain: "app.example.com",90 Domain: "app.example.com",
@@ -137,7 +148,12 @@ func TestBootstrap_MissingDomain(t *testing.T) {
137 containerSvc := inmocks.NewMockContainerService(t)148 containerSvc := inmocks.NewMockContainerService(t)
138 secretSvc := inmocks.NewMockSecretService(t)149 secretSvc := inmocks.NewMockSecretService(t)
139 150
140- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)151+ handler := newTestHandler(t, func(d *HandlerDeps) {
152+ d.ConfigSvc = configSvc
153+ d.AuthSvc = authSvc
154+ d.ContainerSvc = containerSvc
155+ d.SecretSvc = secretSvc
156+ })
141 157
142 body, err := json.Marshal(dto.BootstrapRequest{Image: "myapp:latest"})158 body, err := json.Marshal(dto.BootstrapRequest{Image: "myapp:latest"})
143 require.NoError(t, err)159 require.NoError(t, err)
@@ -158,7 +174,12 @@ func TestBootstrap_MissingImage(t *testing.T) {
158 containerSvc := inmocks.NewMockContainerService(t)174 containerSvc := inmocks.NewMockContainerService(t)
159 secretSvc := inmocks.NewMockSecretService(t)175 secretSvc := inmocks.NewMockSecretService(t)
160 176
161- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)177+ handler := newTestHandler(t, func(d *HandlerDeps) {
178+ d.ConfigSvc = configSvc
179+ d.AuthSvc = authSvc
180+ d.ContainerSvc = containerSvc
181+ d.SecretSvc = secretSvc
182+ })
162 183
163 body, err := json.Marshal(dto.BootstrapRequest{Domain: "app.example.com"})184 body, err := json.Marshal(dto.BootstrapRequest{Domain: "app.example.com"})
164 require.NoError(t, err)185 require.NoError(t, err)
@@ -179,7 +200,12 @@ func TestBootstrap_NormalizesImageBeforeStoring(t *testing.T) {
179 containerSvc := inmocks.NewMockContainerService(t)200 containerSvc := inmocks.NewMockContainerService(t)
180 secretSvc := inmocks.NewMockSecretService(t)201 secretSvc := inmocks.NewMockSecretService(t)
181 202
182- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)203+ handler := newTestHandler(t, func(d *HandlerDeps) {
204+ d.ConfigSvc = configSvc
205+ d.AuthSvc = authSvc
206+ d.ContainerSvc = containerSvc
207+ d.SecretSvc = secretSvc
208+ })
183 209
184 payload := dto.BootstrapRequest{Domain: "app.example.com", Image: "pitlane:v1"}210 payload := dto.BootstrapRequest{Domain: "app.example.com", Image: "pitlane:v1"}
185 configSvc.EXPECT().GetRegistryDomain().Return("reg.bnema.dev").Once()211 configSvc.EXPECT().GetRegistryDomain().Return("reg.bnema.dev").Once()
@@ -225,7 +251,12 @@ func TestBootstrap_AddRouteValidationErrors(t *testing.T) {
225 containerSvc := inmocks.NewMockContainerService(t)251 containerSvc := inmocks.NewMockContainerService(t)
226 secretSvc := inmocks.NewMockSecretService(t)252 secretSvc := inmocks.NewMockSecretService(t)
227 253
228- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)254+ handler := newTestHandler(t, func(d *HandlerDeps) {
255+ d.ConfigSvc = configSvc
256+ d.AuthSvc = authSvc
257+ d.ContainerSvc = containerSvc
258+ d.SecretSvc = secretSvc
259+ })
229 260
230 payload := dto.BootstrapRequest{Domain: "app.example.com", Image: "myapp:latest"}261 payload := dto.BootstrapRequest{Domain: "app.example.com", Image: "myapp:latest"}
231 configSvc.EXPECT().GetRegistryDomain().Return("reg.bnema.dev").Once()262 configSvc.EXPECT().GetRegistryDomain().Return("reg.bnema.dev").Once()
@@ -252,7 +283,12 @@ func TestBootstrap_PartialFailure_AttachmentError(t *testing.T) {
252 containerSvc := inmocks.NewMockContainerService(t)283 containerSvc := inmocks.NewMockContainerService(t)
253 secretSvc := inmocks.NewMockSecretService(t)284 secretSvc := inmocks.NewMockSecretService(t)
254 285
255- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)286+ handler := newTestHandler(t, func(d *HandlerDeps) {
287+ d.ConfigSvc = configSvc
288+ d.AuthSvc = authSvc
289+ d.ContainerSvc = containerSvc
290+ d.SecretSvc = secretSvc
291+ })
256 292
257 payload := dto.BootstrapRequest{293 payload := dto.BootstrapRequest{
258 Domain: "app.example.com",294 Domain: "app.example.com",
M internal/adapters/in/http/admin/handler.go +31 -37
@@ -131,48 +131,42 @@ func toDatabaseInfoResponse(db domain.DBInfo) dto.DatabaseInfo {
131131 }
132132 }
133133
134-// NewHandler creates a new admin HTTP handler.
135-func NewHandler(
136- configSvc in.ConfigService,
137- authSvc in.AuthService,
138- containerSvc in.ContainerService,
139- healthSvc in.HealthService,
140- secretSvc in.SecretService,
141- logSvc in.LogService,
142- registrySvc registryDeployService,
143- eventBus out.EventPublisher,
144- log zerowrap.Logger,
145- backupSvc in.BackupService,
146- previewSvc previewService,
147- imageSvcs ...in.ImageService,
148-) *Handler {
149- var imageSvc in.ImageService
150- if len(imageSvcs) > 0 {
151- imageSvc = imageSvcs[0]
152- }
134+// HandlerDeps contains all dependencies for the admin HTTP handler.
135+type HandlerDeps struct {
136+ ConfigSvc in.ConfigService
137+ AuthSvc in.AuthService
138+ ContainerSvc in.ContainerService
139+ HealthSvc in.HealthService
140+ SecretSvc in.SecretService
141+ LogSvc in.LogService
142+ RegistrySvc registryDeployService
143+ EventBus out.EventPublisher
144+ Log zerowrap.Logger
145+ BackupSvc in.BackupService
146+ PreviewSvc previewService
147+ ImageSvc in.ImageService
148+ VolumeSvc in.VolumeService
149+}
153150
151+// NewHandler creates a new admin HTTP handler.
152+func NewHandler(deps HandlerDeps) *Handler {
154153 return &Handler{
155- configSvc: configSvc,
156- authSvc: authSvc,
157- containerSvc: containerSvc,
158- backupSvc: backupSvc,
159- imageSvc: imageSvc,
160- healthSvc: healthSvc,
161- secretSvc: secretSvc,
162- logSvc: logSvc,
163- registrySvc: registrySvc,
164- previewSvc: previewSvc,
165- eventBus: eventBus,
166- log: log,
154+ configSvc: deps.ConfigSvc,
155+ authSvc: deps.AuthSvc,
156+ containerSvc: deps.ContainerSvc,
157+ backupSvc: deps.BackupSvc,
158+ imageSvc: deps.ImageSvc,
159+ healthSvc: deps.HealthSvc,
160+ secretSvc: deps.SecretSvc,
161+ logSvc: deps.LogSvc,
162+ volumeSvc: deps.VolumeSvc,
163+ registrySvc: deps.RegistrySvc,
164+ previewSvc: deps.PreviewSvc,
165+ eventBus: deps.EventBus,
166+ log: deps.Log,
167167 }
168168 }
169169
170-// WithVolumeService sets the volume service on the handler.
171-func (h *Handler) WithVolumeService(volumeSvc in.VolumeService) *Handler {
172- h.volumeSvc = volumeSvc
173- return h
174-}
175-
176170 // RegisterRoutes registers the admin routes on the given mux.
177171 func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
178172 mux.HandleFunc("/admin/", h.handleAdminRoutes)
@@ -131,48 +131,42 @@ func toDatabaseInfoResponse(db domain.DBInfo) dto.DatabaseInfo {
131 }131 }
132 }132 }
133 133
134-// NewHandler creates a new admin HTTP handler.134+// HandlerDeps contains all dependencies for the admin HTTP handler.
135-func NewHandler(135+type HandlerDeps struct {
136- configSvc in.ConfigService,136+ ConfigSvc in.ConfigService
137- authSvc in.AuthService,137+ AuthSvc in.AuthService
138- containerSvc in.ContainerService,138+ ContainerSvc in.ContainerService
139- healthSvc in.HealthService,139+ HealthSvc in.HealthService
140- secretSvc in.SecretService,140+ SecretSvc in.SecretService
141- logSvc in.LogService,141+ LogSvc in.LogService
142- registrySvc registryDeployService,142+ RegistrySvc registryDeployService
143- eventBus out.EventPublisher,143+ EventBus out.EventPublisher
144- log zerowrap.Logger,144+ Log zerowrap.Logger
145- backupSvc in.BackupService,145+ BackupSvc in.BackupService
146- previewSvc previewService,146+ PreviewSvc previewService
147- imageSvcs ...in.ImageService,147+ ImageSvc in.ImageService
148-) *Handler {148+ VolumeSvc in.VolumeService
149- var imageSvc in.ImageService149+}
150- if len(imageSvcs) > 0 {
151- imageSvc = imageSvcs[0]
152- }
153 150
151+// NewHandler creates a new admin HTTP handler.
152+func NewHandler(deps HandlerDeps) *Handler {
154 return &Handler{153 return &Handler{
155- configSvc: configSvc,154+ configSvc: deps.ConfigSvc,
156- authSvc: authSvc,155+ authSvc: deps.AuthSvc,
157- containerSvc: containerSvc,156+ containerSvc: deps.ContainerSvc,
158- backupSvc: backupSvc,157+ backupSvc: deps.BackupSvc,
159- imageSvc: imageSvc,158+ imageSvc: deps.ImageSvc,
160- healthSvc: healthSvc,159+ healthSvc: deps.HealthSvc,
161- secretSvc: secretSvc,160+ secretSvc: deps.SecretSvc,
162- logSvc: logSvc,161+ logSvc: deps.LogSvc,
163- registrySvc: registrySvc,162+ volumeSvc: deps.VolumeSvc,
164- previewSvc: previewSvc,163+ registrySvc: deps.RegistrySvc,
165- eventBus: eventBus,164+ previewSvc: deps.PreviewSvc,
166- log: log,165+ eventBus: deps.EventBus,
166+ log: deps.Log,
167 }167 }
168 }168 }
169 169
170-// WithVolumeService sets the volume service on the handler.
171-func (h *Handler) WithVolumeService(volumeSvc in.VolumeService) *Handler {
172- h.volumeSvc = volumeSvc
173- return h
174-}
175-
176 // RegisterRoutes registers the admin routes on the given mux.170 // RegisterRoutes registers the admin routes on the given mux.
177 func (h *Handler) RegisterRoutes(mux *http.ServeMux) {171 func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
178 mux.HandleFunc("/admin/", h.handleAdminRoutes)172 mux.HandleFunc("/admin/", h.handleAdminRoutes)
M internal/adapters/in/http/admin/handler_test.go +377 -59
@@ -50,6 +50,22 @@ func ctxWithScopes(scopes ...string) context.Context {
5050 return context.WithValue(ctx, domain.ContextKeyScopes, scopes)
5151 }
5252
53+func newTestHandler(t *testing.T, opts ...func(*HandlerDeps)) *Handler {
54+ t.Helper()
55+ deps := HandlerDeps{
56+ ConfigSvc: inmocks.NewMockConfigService(t),
57+ AuthSvc: inmocks.NewMockAuthService(t),
58+ ContainerSvc: inmocks.NewMockContainerService(t),
59+ HealthSvc: inmocks.NewMockHealthService(t),
60+ SecretSvc: inmocks.NewMockSecretService(t),
61+ Log: testLogger(),
62+ }
63+ for _, opt := range opts {
64+ opt(&deps)
65+ }
66+ return NewHandler(deps)
67+}
68+
5369 // Routes endpoint tests
5470
5571 func TestHandler_RoutesGet_RequiresReadScope(t *testing.T) {
@@ -58,7 +74,13 @@ func TestHandler_RoutesGet_RequiresReadScope(t *testing.T) {
5874 containerSvc := inmocks.NewMockContainerService(t)
5975 secretSvc := inmocks.NewMockSecretService(t)
6076
61- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
77+ handler := newTestHandler(t, func(d *HandlerDeps) {
78+ d.ConfigSvc = configSvc
79+ d.AuthSvc = authSvc
80+ d.ContainerSvc = containerSvc
81+ d.SecretSvc = secretSvc
82+ d.RegistrySvc = inmocks.NewMockRegistryService(t)
83+ })
6284
6385 tests := []struct {
6486 name string
@@ -119,9 +141,13 @@ func TestHandler_RoutesPost_RequiresWriteScope(t *testing.T) {
119141 authSvc := inmocks.NewMockAuthService(t)
120142 containerSvc := inmocks.NewMockContainerService(t)
121143 secretSvc := inmocks.NewMockSecretService(t)
122- registrySvc := inmocks.NewMockRegistryService(t)
123144
124- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)
145+ handler := newTestHandler(t, func(d *HandlerDeps) {
146+ d.ConfigSvc = configSvc
147+ d.AuthSvc = authSvc
148+ d.ContainerSvc = containerSvc
149+ d.SecretSvc = secretSvc
150+ })
125151
126152 routeJSON := `{"domain": "app.example.com", "image": "myapp:latest"}`
127153
@@ -179,9 +205,13 @@ func TestHandler_RoutesPut_RequiresWriteScope(t *testing.T) {
179205 authSvc := inmocks.NewMockAuthService(t)
180206 containerSvc := inmocks.NewMockContainerService(t)
181207 secretSvc := inmocks.NewMockSecretService(t)
182- registrySvc := inmocks.NewMockRegistryService(t)
183208
184- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)
209+ handler := newTestHandler(t, func(d *HandlerDeps) {
210+ d.ConfigSvc = configSvc
211+ d.AuthSvc = authSvc
212+ d.ContainerSvc = containerSvc
213+ d.SecretSvc = secretSvc
214+ })
185215
186216 routeJSON := `{"image": "myapp:v2"}`
187217
@@ -227,7 +257,13 @@ func TestHandler_RoutesPost_AllowsRouteCreationWithoutRegistryManifest(t *testin
227257 registrySvc := inmocks.NewMockRegistryService(t)
228258 route := domain.Route{Domain: "app.example.com", Image: "myapp:latest"}
229259
230- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)
260+ handler := newTestHandler(t, func(d *HandlerDeps) {
261+ d.ConfigSvc = configSvc
262+ d.AuthSvc = authSvc
263+ d.ContainerSvc = containerSvc
264+ d.SecretSvc = secretSvc
265+ d.RegistrySvc = registrySvc
266+ })
231267
232268 configSvc.EXPECT().AddRoute(mock.Anything, route).Return(nil).Once()
233269
@@ -250,7 +286,13 @@ func TestHandler_RoutesPut_AllowsRouteUpdateWithoutRegistryManifest(t *testing.T
250286 registrySvc := inmocks.NewMockRegistryService(t)
251287 route := domain.Route{Domain: "app.example.com", Image: "myapp:v2"}
252288
253- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)
289+ handler := newTestHandler(t, func(d *HandlerDeps) {
290+ d.ConfigSvc = configSvc
291+ d.AuthSvc = authSvc
292+ d.ContainerSvc = containerSvc
293+ d.SecretSvc = secretSvc
294+ d.RegistrySvc = registrySvc
295+ })
254296
255297 configSvc.EXPECT().UpdateRoute(mock.Anything, route).Return(nil).Once()
256298
@@ -271,7 +313,12 @@ func TestHandler_AttachmentsByImageGet_OneTarget(t *testing.T) {
271313 containerSvc := inmocks.NewMockContainerService(t)
272314 secretSvc := inmocks.NewMockSecretService(t)
273315
274- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
316+ handler := newTestHandler(t, func(d *HandlerDeps) {
317+ d.ConfigSvc = configSvc
318+ d.AuthSvc = authSvc
319+ d.ContainerSvc = containerSvc
320+ d.SecretSvc = secretSvc
321+ })
275322
276323 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "postgres:16").Return([]string{"app.example.com"}).Once()
277324
@@ -291,7 +338,12 @@ func TestHandler_AttachmentsByImageGet_MultipleTargets(t *testing.T) {
291338 containerSvc := inmocks.NewMockContainerService(t)
292339 secretSvc := inmocks.NewMockSecretService(t)
293340
294- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
341+ handler := newTestHandler(t, func(d *HandlerDeps) {
342+ d.ConfigSvc = configSvc
343+ d.AuthSvc = authSvc
344+ d.ContainerSvc = containerSvc
345+ d.SecretSvc = secretSvc
346+ })
295347
296348 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "postgres").Return([]string{"app.example.com", "workers"}).Once()
297349
@@ -311,7 +363,12 @@ func TestHandler_AttachmentsByImageGet_URLDecodedImage(t *testing.T) {
311363 containerSvc := inmocks.NewMockContainerService(t)
312364 secretSvc := inmocks.NewMockSecretService(t)
313365
314- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
366+ handler := newTestHandler(t, func(d *HandlerDeps) {
367+ d.ConfigSvc = configSvc
368+ d.AuthSvc = authSvc
369+ d.ContainerSvc = containerSvc
370+ d.SecretSvc = secretSvc
371+ })
315372
316373 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "registry/org/image:tag").Return([]string{"app.example.com"}).Once()
317374
@@ -331,7 +388,12 @@ func TestHandler_AttachmentsByImageGet_EmptyResultSet(t *testing.T) {
331388 containerSvc := inmocks.NewMockContainerService(t)
332389 secretSvc := inmocks.NewMockSecretService(t)
333390
334- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
391+ handler := newTestHandler(t, func(d *HandlerDeps) {
392+ d.ConfigSvc = configSvc
393+ d.AuthSvc = authSvc
394+ d.ContainerSvc = containerSvc
395+ d.SecretSvc = secretSvc
396+ })
335397
336398 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "missing:latest").Return([]string{}).Once()
337399
@@ -351,7 +413,12 @@ func TestHandler_AttachmentsByImageGet_MissingImagePath(t *testing.T) {
351413 containerSvc := inmocks.NewMockContainerService(t)
352414 secretSvc := inmocks.NewMockSecretService(t)
353415
354- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
416+ handler := newTestHandler(t, func(d *HandlerDeps) {
417+ d.ConfigSvc = configSvc
418+ d.AuthSvc = authSvc
419+ d.ContainerSvc = containerSvc
420+ d.SecretSvc = secretSvc
421+ })
355422
356423 req := httptest.NewRequest("GET", "/admin/attachments/by-image", nil)
357424 req = req.WithContext(ctxWithScopes("admin:config:read"))
@@ -369,7 +436,12 @@ func TestHandler_AttachmentsByImageGet_InvalidEncoding(t *testing.T) {
369436 containerSvc := inmocks.NewMockContainerService(t)
370437 secretSvc := inmocks.NewMockSecretService(t)
371438
372- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
439+ handler := newTestHandler(t, func(d *HandlerDeps) {
440+ d.ConfigSvc = configSvc
441+ d.AuthSvc = authSvc
442+ d.ContainerSvc = containerSvc
443+ d.SecretSvc = secretSvc
444+ })
373445
374446 req := httptest.NewRequest("GET", "/admin/attachments/by-image/placeholder", nil)
375447 req.URL = &url.URL{Path: "/admin/attachments/by-image/%zz"}
@@ -388,7 +460,12 @@ func TestHandler_AttachmentsByImageGet_RequiresReadScope(t *testing.T) {
388460 containerSvc := inmocks.NewMockContainerService(t)
389461 secretSvc := inmocks.NewMockSecretService(t)
390462
391- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
463+ handler := newTestHandler(t, func(d *HandlerDeps) {
464+ d.ConfigSvc = configSvc
465+ d.AuthSvc = authSvc
466+ d.ContainerSvc = containerSvc
467+ d.SecretSvc = secretSvc
468+ })
392469
393470 req := httptest.NewRequest("GET", "/admin/attachments/by-image/postgres:16", nil)
394471 req = req.WithContext(ctxWithScopes("admin:routes:read"))
@@ -406,7 +483,12 @@ func TestHandler_RoutesDelete_RequiresWriteScope(t *testing.T) {
406483 containerSvc := inmocks.NewMockContainerService(t)
407484 secretSvc := inmocks.NewMockSecretService(t)
408485
409- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
486+ handler := newTestHandler(t, func(d *HandlerDeps) {
487+ d.ConfigSvc = configSvc
488+ d.AuthSvc = authSvc
489+ d.ContainerSvc = containerSvc
490+ d.SecretSvc = secretSvc
491+ })
410492
411493 tests := []struct {
412494 name string
@@ -450,7 +532,12 @@ func TestHandler_SecretsGet_RequiresReadScope(t *testing.T) {
450532 containerSvc := inmocks.NewMockContainerService(t)
451533 secretSvc := inmocks.NewMockSecretService(t)
452534
453- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
535+ handler := newTestHandler(t, func(d *HandlerDeps) {
536+ d.ConfigSvc = configSvc
537+ d.AuthSvc = authSvc
538+ d.ContainerSvc = containerSvc
539+ d.SecretSvc = secretSvc
540+ })
454541
455542 tests := []struct {
456543 name string
@@ -507,7 +594,12 @@ func TestHandler_SecretsPost_RequiresWriteScope(t *testing.T) {
507594 containerSvc := inmocks.NewMockContainerService(t)
508595 secretSvc := inmocks.NewMockSecretService(t)
509596
510- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
597+ handler := newTestHandler(t, func(d *HandlerDeps) {
598+ d.ConfigSvc = configSvc
599+ d.AuthSvc = authSvc
600+ d.ContainerSvc = containerSvc
601+ d.SecretSvc = secretSvc
602+ })
511603
512604 secretsJSON := `{"API_KEY": "secret123"}`
513605
@@ -556,7 +648,12 @@ func TestHandler_SecretsDelete_RequiresWriteScope(t *testing.T) {
556648 containerSvc := inmocks.NewMockContainerService(t)
557649 secretSvc := inmocks.NewMockSecretService(t)
558650
559- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
651+ handler := newTestHandler(t, func(d *HandlerDeps) {
652+ d.ConfigSvc = configSvc
653+ d.AuthSvc = authSvc
654+ d.ContainerSvc = containerSvc
655+ d.SecretSvc = secretSvc
656+ })
560657
561658 tests := []struct {
562659 name string
@@ -600,7 +697,12 @@ func TestHandler_AttachmentSecretsPost(t *testing.T) {
600697 containerSvc := inmocks.NewMockContainerService(t)
601698 secretSvc := inmocks.NewMockSecretService(t)
602699
603- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
700+ handler := newTestHandler(t, func(d *HandlerDeps) {
701+ d.ConfigSvc = configSvc
702+ d.AuthSvc = authSvc
703+ d.ContainerSvc = containerSvc
704+ d.SecretSvc = secretSvc
705+ })
604706
605707 secretSvc.EXPECT().SetAttachment(mock.Anything, "app.example.com", "redis", mock.AnythingOfType("map[string]string")).Return(nil)
606708
@@ -626,7 +728,12 @@ func TestHandler_AttachmentSecretsDelete(t *testing.T) {
626728 containerSvc := inmocks.NewMockContainerService(t)
627729 secretSvc := inmocks.NewMockSecretService(t)
628730
629- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
731+ handler := newTestHandler(t, func(d *HandlerDeps) {
732+ d.ConfigSvc = configSvc
733+ d.AuthSvc = authSvc
734+ d.ContainerSvc = containerSvc
735+ d.SecretSvc = secretSvc
736+ })
630737
631738 secretSvc.EXPECT().DeleteAttachment(mock.Anything, "app.example.com", "redis", "REDIS_URL").Return(nil)
632739
@@ -652,7 +759,13 @@ func TestHandler_Bootstrap_HappyPath(t *testing.T) {
652759 secretSvc := inmocks.NewMockSecretService(t)
653760 registrySvc := inmocks.NewMockRegistryService(t)
654761
655- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)
762+ handler := newTestHandler(t, func(d *HandlerDeps) {
763+ d.ConfigSvc = configSvc
764+ d.AuthSvc = authSvc
765+ d.ContainerSvc = containerSvc
766+ d.SecretSvc = secretSvc
767+ d.RegistrySvc = registrySvc
768+ })
656769
657770 configSvc.EXPECT().GetRegistryDomain().Return("reg.bnema.dev").Once()
658771 configSvc.EXPECT().AddRoute(mock.Anything, domain.Route{Domain: "app.example.com", Image: "reg.bnema.dev/myapp"}).Return(nil).Once()
@@ -699,7 +812,12 @@ func TestHandler_Bootstrap_RequiresPermissions(t *testing.T) {
699812 containerSvc := inmocks.NewMockContainerService(t)
700813 secretSvc := inmocks.NewMockSecretService(t)
701814
702- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
815+ handler := newTestHandler(t, func(d *HandlerDeps) {
816+ d.ConfigSvc = configSvc
817+ d.AuthSvc = authSvc
818+ d.ContainerSvc = containerSvc
819+ d.SecretSvc = secretSvc
820+ })
703821
704822 tests := []struct {
705823 name string
@@ -741,7 +859,12 @@ func TestHandler_Status_RequiresReadScope(t *testing.T) {
741859 containerSvc := inmocks.NewMockContainerService(t)
742860 secretSvc := inmocks.NewMockSecretService(t)
743861
744- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
862+ handler := newTestHandler(t, func(d *HandlerDeps) {
863+ d.ConfigSvc = configSvc
864+ d.AuthSvc = authSvc
865+ d.ContainerSvc = containerSvc
866+ d.SecretSvc = secretSvc
867+ })
745868
746869 tests := []struct {
747870 name string
@@ -795,7 +918,12 @@ func TestHandler_Config_RequiresReadScope(t *testing.T) {
795918 containerSvc := inmocks.NewMockContainerService(t)
796919 secretSvc := inmocks.NewMockSecretService(t)
797920
798- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
921+ handler := newTestHandler(t, func(d *HandlerDeps) {
922+ d.ConfigSvc = configSvc
923+ d.AuthSvc = authSvc
924+ d.ContainerSvc = containerSvc
925+ d.SecretSvc = secretSvc
926+ })
799927
800928 tests := []struct {
801929 name string
@@ -852,7 +980,12 @@ func TestHandler_Reload_RequiresWriteScope(t *testing.T) {
852980 containerSvc := inmocks.NewMockContainerService(t)
853981 secretSvc := inmocks.NewMockSecretService(t)
854982
855- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
983+ handler := newTestHandler(t, func(d *HandlerDeps) {
984+ d.ConfigSvc = configSvc
985+ d.AuthSvc = authSvc
986+ d.ContainerSvc = containerSvc
987+ d.SecretSvc = secretSvc
988+ })
856989
857990 tests := []struct {
858991 name string
@@ -906,7 +1039,12 @@ func TestHandler_RoutesGet_ReturnsRoutes(t *testing.T) {
9061039 containerSvc := inmocks.NewMockContainerService(t)
9071040 secretSvc := inmocks.NewMockSecretService(t)
9081041
909- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1042+ handler := newTestHandler(t, func(d *HandlerDeps) {
1043+ d.ConfigSvc = configSvc
1044+ d.AuthSvc = authSvc
1045+ d.ContainerSvc = containerSvc
1046+ d.SecretSvc = secretSvc
1047+ })
9101048
9111049 expectedRoutes := []domain.Route{
9121050 {Domain: "app1.example.com", Image: "app1:latest"},
@@ -941,7 +1079,12 @@ func TestHandler_RoutesGet_SingleRoute(t *testing.T) {
9411079 containerSvc := inmocks.NewMockContainerService(t)
9421080 secretSvc := inmocks.NewMockSecretService(t)
9431081
944- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1082+ handler := newTestHandler(t, func(d *HandlerDeps) {
1083+ d.ConfigSvc = configSvc
1084+ d.AuthSvc = authSvc
1085+ d.ContainerSvc = containerSvc
1086+ d.SecretSvc = secretSvc
1087+ })
9451088
9461089 configSvc.EXPECT().GetRoute(mock.Anything, "app.example.com").Return(&domain.Route{
9471090 Domain: "app.example.com", Image: "app:latest",
@@ -972,7 +1115,12 @@ func TestHandler_RoutesGet_NotFound(t *testing.T) {
9721115 containerSvc := inmocks.NewMockContainerService(t)
9731116 secretSvc := inmocks.NewMockSecretService(t)
9741117
975- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1118+ handler := newTestHandler(t, func(d *HandlerDeps) {
1119+ d.ConfigSvc = configSvc
1120+ d.AuthSvc = authSvc
1121+ d.ContainerSvc = containerSvc
1122+ d.SecretSvc = secretSvc
1123+ })
9761124
9771125 configSvc.EXPECT().GetRoute(mock.Anything, "nonexistent.example.com").Return(nil, domain.ErrRouteNotFound)
9781126
@@ -1016,7 +1164,13 @@ func TestHandler_RoutesPost_MissingFields(t *testing.T) {
10161164 secretSvc := inmocks.NewMockSecretService(t)
10171165 registrySvc := inmocks.NewMockRegistryService(t)
10181166
1019- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)
1167+ handler := newTestHandler(t, func(d *HandlerDeps) {
1168+ d.ConfigSvc = configSvc
1169+ d.AuthSvc = authSvc
1170+ d.ContainerSvc = containerSvc
1171+ d.SecretSvc = secretSvc
1172+ d.RegistrySvc = registrySvc
1173+ })
10201174
10211175 configSvc.EXPECT().AddRoute(mock.Anything, mock.Anything).Return(tt.mockErr)
10221176
@@ -1037,7 +1191,12 @@ func TestHandler_RoutesPut_MissingDomain(t *testing.T) {
10371191 containerSvc := inmocks.NewMockContainerService(t)
10381192 secretSvc := inmocks.NewMockSecretService(t)
10391193
1040- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1194+ handler := newTestHandler(t, func(d *HandlerDeps) {
1195+ d.ConfigSvc = configSvc
1196+ d.AuthSvc = authSvc
1197+ d.ContainerSvc = containerSvc
1198+ d.SecretSvc = secretSvc
1199+ })
10411200
10421201 req := httptest.NewRequest("PUT", "/admin/routes/", bytes.NewBufferString(`{"image": "app:latest"}`))
10431202 req = req.WithContext(ctxWithScopes("admin:routes:write"))
@@ -1054,7 +1213,12 @@ func TestHandler_RoutesDelete_MissingDomain(t *testing.T) {
10541213 containerSvc := inmocks.NewMockContainerService(t)
10551214 secretSvc := inmocks.NewMockSecretService(t)
10561215
1057- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1216+ handler := newTestHandler(t, func(d *HandlerDeps) {
1217+ d.ConfigSvc = configSvc
1218+ d.AuthSvc = authSvc
1219+ d.ContainerSvc = containerSvc
1220+ d.SecretSvc = secretSvc
1221+ })
10581222
10591223 req := httptest.NewRequest("DELETE", "/admin/routes/", nil)
10601224 req = req.WithContext(ctxWithScopes("admin:routes:write"))
@@ -1071,7 +1235,12 @@ func TestHandler_Secrets_MissingDomain(t *testing.T) {
10711235 containerSvc := inmocks.NewMockContainerService(t)
10721236 secretSvc := inmocks.NewMockSecretService(t)
10731237
1074- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1238+ handler := newTestHandler(t, func(d *HandlerDeps) {
1239+ d.ConfigSvc = configSvc
1240+ d.AuthSvc = authSvc
1241+ d.ContainerSvc = containerSvc
1242+ d.SecretSvc = secretSvc
1243+ })
10751244
10761245 req := httptest.NewRequest("GET", "/admin/secrets/", nil)
10771246 req = req.WithContext(ctxWithScopes("admin:secrets:read"))
@@ -1088,7 +1257,12 @@ func TestHandler_SecretsDelete_MissingKey(t *testing.T) {
10881257 containerSvc := inmocks.NewMockContainerService(t)
10891258 secretSvc := inmocks.NewMockSecretService(t)
10901259
1091- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1260+ handler := newTestHandler(t, func(d *HandlerDeps) {
1261+ d.ConfigSvc = configSvc
1262+ d.AuthSvc = authSvc
1263+ d.ContainerSvc = containerSvc
1264+ d.SecretSvc = secretSvc
1265+ })
10921266
10931267 req := httptest.NewRequest("DELETE", "/admin/secrets/app.example.com", nil)
10941268 req = req.WithContext(ctxWithScopes("admin:secrets:write"))
@@ -1105,7 +1279,12 @@ func TestHandler_MethodNotAllowed(t *testing.T) {
11051279 containerSvc := inmocks.NewMockContainerService(t)
11061280 secretSvc := inmocks.NewMockSecretService(t)
11071281
1108- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1282+ handler := newTestHandler(t, func(d *HandlerDeps) {
1283+ d.ConfigSvc = configSvc
1284+ d.AuthSvc = authSvc
1285+ d.ContainerSvc = containerSvc
1286+ d.SecretSvc = secretSvc
1287+ })
11091288
11101289 tests := []struct {
11111290 method string
@@ -1138,7 +1317,12 @@ func TestHandler_Restart_InvalidDomain(t *testing.T) {
11381317 containerSvc := inmocks.NewMockContainerService(t)
11391318 secretSvc := inmocks.NewMockSecretService(t)
11401319
1141- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1320+ handler := newTestHandler(t, func(d *HandlerDeps) {
1321+ d.ConfigSvc = configSvc
1322+ d.AuthSvc = authSvc
1323+ d.ContainerSvc = containerSvc
1324+ d.SecretSvc = secretSvc
1325+ })
11421326
11431327 tests := []struct {
11441328 name string
@@ -1168,7 +1352,12 @@ func TestHandler_Restart_ContainerNotFound(t *testing.T) {
11681352 containerSvc := inmocks.NewMockContainerService(t)
11691353 secretSvc := inmocks.NewMockSecretService(t)
11701354
1171- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1355+ handler := newTestHandler(t, func(d *HandlerDeps) {
1356+ d.ConfigSvc = configSvc
1357+ d.AuthSvc = authSvc
1358+ d.ContainerSvc = containerSvc
1359+ d.SecretSvc = secretSvc
1360+ })
11721361
11731362 containerSvc.EXPECT().Restart(mock.Anything, "nonexistent.example.com", false).Return(domain.ErrContainerNotFound)
11741363
@@ -1187,7 +1376,12 @@ func TestHandler_Restart_GenericError(t *testing.T) {
11871376 containerSvc := inmocks.NewMockContainerService(t)
11881377 secretSvc := inmocks.NewMockSecretService(t)
11891378
1190- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1379+ handler := newTestHandler(t, func(d *HandlerDeps) {
1380+ d.ConfigSvc = configSvc
1381+ d.AuthSvc = authSvc
1382+ d.ContainerSvc = containerSvc
1383+ d.SecretSvc = secretSvc
1384+ })
11911385
11921386 containerSvc.EXPECT().Restart(mock.Anything, "test.example.com", false).Return(fmt.Errorf("docker daemon error: secret details"))
11931387
@@ -1209,7 +1403,12 @@ func TestHandler_Restart_Success(t *testing.T) {
12091403 containerSvc := inmocks.NewMockContainerService(t)
12101404 secretSvc := inmocks.NewMockSecretService(t)
12111405
1212- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1406+ handler := newTestHandler(t, func(d *HandlerDeps) {
1407+ d.ConfigSvc = configSvc
1408+ d.AuthSvc = authSvc
1409+ d.ContainerSvc = containerSvc
1410+ d.SecretSvc = secretSvc
1411+ })
12131412
12141413 tests := []struct {
12151414 name string
@@ -1256,7 +1455,12 @@ func TestHandler_Tags_InvalidRepository(t *testing.T) {
12561455 containerSvc := inmocks.NewMockContainerService(t)
12571456 secretSvc := inmocks.NewMockSecretService(t)
12581457
1259- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1458+ handler := newTestHandler(t, func(d *HandlerDeps) {
1459+ d.ConfigSvc = configSvc
1460+ d.AuthSvc = authSvc
1461+ d.ContainerSvc = containerSvc
1462+ d.SecretSvc = secretSvc
1463+ })
12601464
12611465 tests := []struct {
12621466 name string
@@ -1287,7 +1491,13 @@ func TestHandler_Tags_Success(t *testing.T) {
12871491 secretSvc := inmocks.NewMockSecretService(t)
12881492 registrySvc := inmocks.NewMockRegistryService(t)
12891493
1290- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)
1494+ handler := newTestHandler(t, func(d *HandlerDeps) {
1495+ d.ConfigSvc = configSvc
1496+ d.AuthSvc = authSvc
1497+ d.ContainerSvc = containerSvc
1498+ d.SecretSvc = secretSvc
1499+ d.RegistrySvc = registrySvc
1500+ })
12911501
12921502 registrySvc.EXPECT().ListTags(mock.Anything, "myapp").Return([]string{"latest", "v1.0"}, nil)
12931503
@@ -1314,7 +1524,13 @@ func TestHandler_Tags_Error(t *testing.T) {
13141524 secretSvc := inmocks.NewMockSecretService(t)
13151525 registrySvc := inmocks.NewMockRegistryService(t)
13161526
1317- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)
1527+ handler := newTestHandler(t, func(d *HandlerDeps) {
1528+ d.ConfigSvc = configSvc
1529+ d.AuthSvc = authSvc
1530+ d.ContainerSvc = containerSvc
1531+ d.SecretSvc = secretSvc
1532+ d.RegistrySvc = registrySvc
1533+ })
13181534
13191535 registrySvc.EXPECT().ListTags(mock.Anything, "myapp").Return(nil, fmt.Errorf("registry error"))
13201536
@@ -1334,7 +1550,12 @@ func TestHandler_AttachmentSecretsPost_RequiresWriteScope(t *testing.T) {
13341550 containerSvc := inmocks.NewMockContainerService(t)
13351551 secretSvc := inmocks.NewMockSecretService(t)
13361552
1337- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1553+ handler := newTestHandler(t, func(d *HandlerDeps) {
1554+ d.ConfigSvc = configSvc
1555+ d.AuthSvc = authSvc
1556+ d.ContainerSvc = containerSvc
1557+ d.SecretSvc = secretSvc
1558+ })
13381559
13391560 body := `{"REDIS_URL": "redis://localhost:6379"}`
13401561 req := httptest.NewRequest("POST", "/admin/secrets/app.example.com/attachments/redis", bytes.NewBufferString(body))
@@ -1352,7 +1573,12 @@ func TestHandler_AttachmentSecretsDelete_RequiresWriteScope(t *testing.T) {
13521573 containerSvc := inmocks.NewMockContainerService(t)
13531574 secretSvc := inmocks.NewMockSecretService(t)
13541575
1355- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1576+ handler := newTestHandler(t, func(d *HandlerDeps) {
1577+ d.ConfigSvc = configSvc
1578+ d.AuthSvc = authSvc
1579+ d.ContainerSvc = containerSvc
1580+ d.SecretSvc = secretSvc
1581+ })
13561582
13571583 req := httptest.NewRequest("DELETE", "/admin/secrets/app.example.com/attachments/redis/REDIS_URL", nil)
13581584 req = req.WithContext(ctxWithScopes("admin:secrets:read"))
@@ -1369,7 +1595,12 @@ func TestHandler_AttachmentSecretsDelete_RequiresKey(t *testing.T) {
13691595 containerSvc := inmocks.NewMockContainerService(t)
13701596 secretSvc := inmocks.NewMockSecretService(t)
13711597
1372- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1598+ handler := newTestHandler(t, func(d *HandlerDeps) {
1599+ d.ConfigSvc = configSvc
1600+ d.AuthSvc = authSvc
1601+ d.ContainerSvc = containerSvc
1602+ d.SecretSvc = secretSvc
1603+ })
13731604
13741605 req := httptest.NewRequest("DELETE", "/admin/secrets/app.example.com/attachments/redis", nil)
13751606 req = req.WithContext(ctxWithScopes("admin:secrets:write"))
@@ -1387,7 +1618,12 @@ func TestHandler_AttachmentSecretsPost_InvalidJSON(t *testing.T) {
13871618 containerSvc := inmocks.NewMockContainerService(t)
13881619 secretSvc := inmocks.NewMockSecretService(t)
13891620
1390- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1621+ handler := newTestHandler(t, func(d *HandlerDeps) {
1622+ d.ConfigSvc = configSvc
1623+ d.AuthSvc = authSvc
1624+ d.ContainerSvc = containerSvc
1625+ d.SecretSvc = secretSvc
1626+ })
13911627
13921628 body := `{not valid json`
13931629 req := httptest.NewRequest("POST", "/admin/secrets/app.example.com/attachments/redis", bytes.NewBufferString(body))
@@ -1406,7 +1642,12 @@ func TestHandler_NotFound(t *testing.T) {
14061642 containerSvc := inmocks.NewMockContainerService(t)
14071643 secretSvc := inmocks.NewMockSecretService(t)
14081644
1409- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
1645+ handler := newTestHandler(t, func(d *HandlerDeps) {
1646+ d.ConfigSvc = configSvc
1647+ d.AuthSvc = authSvc
1648+ d.ContainerSvc = containerSvc
1649+ d.SecretSvc = secretSvc
1650+ })
14101651
14111652 req := httptest.NewRequest("GET", "/admin/unknown", nil)
14121653 req = req.WithContext(ctxWithScopes("admin:*:*"))
@@ -1426,7 +1667,13 @@ func TestHandler_BackupsStatus(t *testing.T) {
14261667
14271668 backupSvc.EXPECT().Status(mock.Anything).Return([]domain.BackupJob{{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted, FilePath: "/var/lib/gordon/backups/private.bak"}}, nil)
14281669
1429- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), backupSvc, nil)
1670+ handler := newTestHandler(t, func(d *HandlerDeps) {
1671+ d.ConfigSvc = configSvc
1672+ d.AuthSvc = authSvc
1673+ d.ContainerSvc = containerSvc
1674+ d.SecretSvc = secretSvc
1675+ d.BackupSvc = backupSvc
1676+ })
14301677
14311678 req := httptest.NewRequest("GET", "/admin/backups/status", nil)
14321679 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1448,7 +1695,13 @@ func TestHandler_BackupsListDomain(t *testing.T) {
14481695
14491696 backupSvc.EXPECT().ListBackups(mock.Anything, "app.example.com").Return([]domain.BackupJob{{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted}}, nil)
14501697
1451- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), backupSvc, nil)
1698+ handler := newTestHandler(t, func(d *HandlerDeps) {
1699+ d.ConfigSvc = configSvc
1700+ d.AuthSvc = authSvc
1701+ d.ContainerSvc = containerSvc
1702+ d.SecretSvc = secretSvc
1703+ d.BackupSvc = backupSvc
1704+ })
14521705
14531706 req := httptest.NewRequest("GET", "/admin/backups/app.example.com", nil)
14541707 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1469,7 +1722,13 @@ func TestHandler_BackupsRunDomain(t *testing.T) {
14691722
14701723 backupSvc.EXPECT().RunBackup(mock.Anything, "app.example.com", "postgres").Return(&domain.BackupResult{Job: domain.BackupJob{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted}}, nil)
14711724
1472- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), backupSvc, nil)
1725+ handler := newTestHandler(t, func(d *HandlerDeps) {
1726+ d.ConfigSvc = configSvc
1727+ d.AuthSvc = authSvc
1728+ d.ContainerSvc = containerSvc
1729+ d.SecretSvc = secretSvc
1730+ d.BackupSvc = backupSvc
1731+ })
14731732
14741733 body := bytes.NewBufferString(`{"db":"postgres"}`)
14751734 req := httptest.NewRequest("POST", "/admin/backups/app.example.com", body)
@@ -1491,7 +1750,13 @@ func TestHandler_BackupsRunDomain_ChunkedBodyIsDecoded(t *testing.T) {
14911750
14921751 backupSvc.EXPECT().RunBackup(mock.Anything, "app.example.com", "postgres").Return(&domain.BackupResult{Job: domain.BackupJob{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted}}, nil)
14931752
1494- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), backupSvc, nil)
1753+ handler := newTestHandler(t, func(d *HandlerDeps) {
1754+ d.ConfigSvc = configSvc
1755+ d.AuthSvc = authSvc
1756+ d.ContainerSvc = containerSvc
1757+ d.SecretSvc = secretSvc
1758+ d.BackupSvc = backupSvc
1759+ })
14951760
14961761 body := bytes.NewBufferString(`{"db":"postgres"}`)
14971762 req := httptest.NewRequest("POST", "/admin/backups/app.example.com", body)
@@ -1514,7 +1779,13 @@ func TestHandler_BackupsDetectDomain(t *testing.T) {
15141779
15151780 backupSvc.EXPECT().DetectDatabases(mock.Anything, "app.example.com").Return([]domain.DBInfo{{Type: domain.DBTypePostgreSQL, Name: "postgres", Port: 5432}}, nil)
15161781
1517- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), backupSvc, nil)
1782+ handler := newTestHandler(t, func(d *HandlerDeps) {
1783+ d.ConfigSvc = configSvc
1784+ d.AuthSvc = authSvc
1785+ d.ContainerSvc = containerSvc
1786+ d.SecretSvc = secretSvc
1787+ d.BackupSvc = backupSvc
1788+ })
15181789
15191790 req := httptest.NewRequest("GET", "/admin/backups/app.example.com/detect", nil)
15201791 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1556,7 +1827,13 @@ func TestHandler_ImagesGet_ReturnsMappedList(t *testing.T) {
15561827 },
15571828 }
15581829
1559- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, imageSvc)
1830+ handler := newTestHandler(t, func(d *HandlerDeps) {
1831+ d.ConfigSvc = configSvc
1832+ d.AuthSvc = authSvc
1833+ d.ContainerSvc = containerSvc
1834+ d.SecretSvc = secretSvc
1835+ d.ImageSvc = imageSvc
1836+ })
15601837
15611838 req := httptest.NewRequest("GET", "/admin/images", nil)
15621839 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1617,7 +1894,13 @@ func TestHandler_ImagesPrune_AcceptsOptionalKeepLast(t *testing.T) {
16171894 },
16181895 }
16191896
1620- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, imageSvc)
1897+ handler := newTestHandler(t, func(d *HandlerDeps) {
1898+ d.ConfigSvc = configSvc
1899+ d.AuthSvc = authSvc
1900+ d.ContainerSvc = containerSvc
1901+ d.SecretSvc = secretSvc
1902+ d.ImageSvc = imageSvc
1903+ })
16211904
16221905 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(tt.body))
16231906 req = req.WithContext(ctxWithScopes("admin:config:write"))
@@ -1646,7 +1929,13 @@ func TestHandler_Images_ErrorMappingForServiceFailures(t *testing.T) {
16461929 },
16471930 }
16481931
1649- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, imageSvc)
1932+ handler := newTestHandler(t, func(d *HandlerDeps) {
1933+ d.ConfigSvc = configSvc
1934+ d.AuthSvc = authSvc
1935+ d.ContainerSvc = containerSvc
1936+ d.SecretSvc = secretSvc
1937+ d.ImageSvc = imageSvc
1938+ })
16501939
16511940 req := httptest.NewRequest("GET", "/admin/images", nil)
16521941 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1671,7 +1960,13 @@ func TestHandler_Images_ErrorMappingForServiceFailures(t *testing.T) {
16711960 },
16721961 }
16731962
1674- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, imageSvc)
1963+ handler := newTestHandler(t, func(d *HandlerDeps) {
1964+ d.ConfigSvc = configSvc
1965+ d.AuthSvc = authSvc
1966+ d.ContainerSvc = containerSvc
1967+ d.SecretSvc = secretSvc
1968+ d.ImageSvc = imageSvc
1969+ })
16751970
16761971 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(`{"keep_last": 2}`))
16771972 req = req.WithContext(ctxWithScopes("admin:config:write"))
@@ -1692,7 +1987,13 @@ func TestHandler_ImagesPrune_ValidationAndAvailability(t *testing.T) {
16921987 containerSvc := inmocks.NewMockContainerService(t)
16931988 secretSvc := inmocks.NewMockSecretService(t)
16941989
1695- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, &stubImageService{})
1990+ handler := newTestHandler(t, func(d *HandlerDeps) {
1991+ d.ConfigSvc = configSvc
1992+ d.AuthSvc = authSvc
1993+ d.ContainerSvc = containerSvc
1994+ d.SecretSvc = secretSvc
1995+ d.ImageSvc = &stubImageService{}
1996+ })
16961997
16971998 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(`{"keep_last": -1}`))
16981999 req = req.WithContext(ctxWithScopes("admin:config:write"))
@@ -1710,7 +2011,12 @@ func TestHandler_ImagesPrune_ValidationAndAvailability(t *testing.T) {
17102011 containerSvc := inmocks.NewMockContainerService(t)
17112012 secretSvc := inmocks.NewMockSecretService(t)
17122013
1713- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)
2014+ handler := newTestHandler(t, func(d *HandlerDeps) {
2015+ d.ConfigSvc = configSvc
2016+ d.AuthSvc = authSvc
2017+ d.ContainerSvc = containerSvc
2018+ d.SecretSvc = secretSvc
2019+ })
17142020
17152021 req := httptest.NewRequest("GET", "/admin/images", nil)
17162022 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1781,7 +2087,13 @@ func TestHandler_ImagesPrune_ScopeDefaults(t *testing.T) {
17812087 },
17822088 }
17832089
1784- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, imageSvc)
2090+ handler := newTestHandler(t, func(d *HandlerDeps) {
2091+ d.ConfigSvc = configSvc
2092+ d.AuthSvc = authSvc
2093+ d.ContainerSvc = containerSvc
2094+ d.SecretSvc = secretSvc
2095+ d.ImageSvc = imageSvc
2096+ })
17852097
17862098 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(tt.body))
17872099 req = req.WithContext(ctxWithScopes("admin:config:write"))
@@ -1806,7 +2118,13 @@ func TestHandler_Images_Authorization(t *testing.T) {
18062118 containerSvc := inmocks.NewMockContainerService(t)
18072119 secretSvc := inmocks.NewMockSecretService(t)
18082120
1809- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, &stubImageService{})
2121+ handler := newTestHandler(t, func(d *HandlerDeps) {
2122+ d.ConfigSvc = configSvc
2123+ d.AuthSvc = authSvc
2124+ d.ContainerSvc = containerSvc
2125+ d.SecretSvc = secretSvc
2126+ d.ImageSvc = &stubImageService{}
2127+ })
18102128
18112129 t.Run("images list requires status:read", func(t *testing.T) {
18122130 req := httptest.NewRequest("GET", "/admin/images", nil)
@@ -50,6 +50,22 @@ func ctxWithScopes(scopes ...string) context.Context {
50 return context.WithValue(ctx, domain.ContextKeyScopes, scopes)50 return context.WithValue(ctx, domain.ContextKeyScopes, scopes)
51 }51 }
52 52
53+func newTestHandler(t *testing.T, opts ...func(*HandlerDeps)) *Handler {
54+ t.Helper()
55+ deps := HandlerDeps{
56+ ConfigSvc: inmocks.NewMockConfigService(t),
57+ AuthSvc: inmocks.NewMockAuthService(t),
58+ ContainerSvc: inmocks.NewMockContainerService(t),
59+ HealthSvc: inmocks.NewMockHealthService(t),
60+ SecretSvc: inmocks.NewMockSecretService(t),
61+ Log: testLogger(),
62+ }
63+ for _, opt := range opts {
64+ opt(&deps)
65+ }
66+ return NewHandler(deps)
67+}
68+
53 // Routes endpoint tests69 // Routes endpoint tests
54 70
55 func TestHandler_RoutesGet_RequiresReadScope(t *testing.T) {71 func TestHandler_RoutesGet_RequiresReadScope(t *testing.T) {
@@ -58,7 +74,13 @@ func TestHandler_RoutesGet_RequiresReadScope(t *testing.T) {
58 containerSvc := inmocks.NewMockContainerService(t)74 containerSvc := inmocks.NewMockContainerService(t)
59 secretSvc := inmocks.NewMockSecretService(t)75 secretSvc := inmocks.NewMockSecretService(t)
60 76
61- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)77+ handler := newTestHandler(t, func(d *HandlerDeps) {
78+ d.ConfigSvc = configSvc
79+ d.AuthSvc = authSvc
80+ d.ContainerSvc = containerSvc
81+ d.SecretSvc = secretSvc
82+ d.RegistrySvc = inmocks.NewMockRegistryService(t)
83+ })
62 84
63 tests := []struct {85 tests := []struct {
64 name string86 name string
@@ -119,9 +141,13 @@ func TestHandler_RoutesPost_RequiresWriteScope(t *testing.T) {
119 authSvc := inmocks.NewMockAuthService(t)141 authSvc := inmocks.NewMockAuthService(t)
120 containerSvc := inmocks.NewMockContainerService(t)142 containerSvc := inmocks.NewMockContainerService(t)
121 secretSvc := inmocks.NewMockSecretService(t)143 secretSvc := inmocks.NewMockSecretService(t)
122- registrySvc := inmocks.NewMockRegistryService(t)
123 144
124- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)145+ handler := newTestHandler(t, func(d *HandlerDeps) {
146+ d.ConfigSvc = configSvc
147+ d.AuthSvc = authSvc
148+ d.ContainerSvc = containerSvc
149+ d.SecretSvc = secretSvc
150+ })
125 151
126 routeJSON := `{"domain": "app.example.com", "image": "myapp:latest"}`152 routeJSON := `{"domain": "app.example.com", "image": "myapp:latest"}`
127 153
@@ -179,9 +205,13 @@ func TestHandler_RoutesPut_RequiresWriteScope(t *testing.T) {
179 authSvc := inmocks.NewMockAuthService(t)205 authSvc := inmocks.NewMockAuthService(t)
180 containerSvc := inmocks.NewMockContainerService(t)206 containerSvc := inmocks.NewMockContainerService(t)
181 secretSvc := inmocks.NewMockSecretService(t)207 secretSvc := inmocks.NewMockSecretService(t)
182- registrySvc := inmocks.NewMockRegistryService(t)
183 208
184- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)209+ handler := newTestHandler(t, func(d *HandlerDeps) {
210+ d.ConfigSvc = configSvc
211+ d.AuthSvc = authSvc
212+ d.ContainerSvc = containerSvc
213+ d.SecretSvc = secretSvc
214+ })
185 215
186 routeJSON := `{"image": "myapp:v2"}`216 routeJSON := `{"image": "myapp:v2"}`
187 217
@@ -227,7 +257,13 @@ func TestHandler_RoutesPost_AllowsRouteCreationWithoutRegistryManifest(t *testin
227 registrySvc := inmocks.NewMockRegistryService(t)257 registrySvc := inmocks.NewMockRegistryService(t)
228 route := domain.Route{Domain: "app.example.com", Image: "myapp:latest"}258 route := domain.Route{Domain: "app.example.com", Image: "myapp:latest"}
229 259
230- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)260+ handler := newTestHandler(t, func(d *HandlerDeps) {
261+ d.ConfigSvc = configSvc
262+ d.AuthSvc = authSvc
263+ d.ContainerSvc = containerSvc
264+ d.SecretSvc = secretSvc
265+ d.RegistrySvc = registrySvc
266+ })
231 267
232 configSvc.EXPECT().AddRoute(mock.Anything, route).Return(nil).Once()268 configSvc.EXPECT().AddRoute(mock.Anything, route).Return(nil).Once()
233 269
@@ -250,7 +286,13 @@ func TestHandler_RoutesPut_AllowsRouteUpdateWithoutRegistryManifest(t *testing.T
250 registrySvc := inmocks.NewMockRegistryService(t)286 registrySvc := inmocks.NewMockRegistryService(t)
251 route := domain.Route{Domain: "app.example.com", Image: "myapp:v2"}287 route := domain.Route{Domain: "app.example.com", Image: "myapp:v2"}
252 288
253- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)289+ handler := newTestHandler(t, func(d *HandlerDeps) {
290+ d.ConfigSvc = configSvc
291+ d.AuthSvc = authSvc
292+ d.ContainerSvc = containerSvc
293+ d.SecretSvc = secretSvc
294+ d.RegistrySvc = registrySvc
295+ })
254 296
255 configSvc.EXPECT().UpdateRoute(mock.Anything, route).Return(nil).Once()297 configSvc.EXPECT().UpdateRoute(mock.Anything, route).Return(nil).Once()
256 298
@@ -271,7 +313,12 @@ func TestHandler_AttachmentsByImageGet_OneTarget(t *testing.T) {
271 containerSvc := inmocks.NewMockContainerService(t)313 containerSvc := inmocks.NewMockContainerService(t)
272 secretSvc := inmocks.NewMockSecretService(t)314 secretSvc := inmocks.NewMockSecretService(t)
273 315
274- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)316+ handler := newTestHandler(t, func(d *HandlerDeps) {
317+ d.ConfigSvc = configSvc
318+ d.AuthSvc = authSvc
319+ d.ContainerSvc = containerSvc
320+ d.SecretSvc = secretSvc
321+ })
275 322
276 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "postgres:16").Return([]string{"app.example.com"}).Once()323 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "postgres:16").Return([]string{"app.example.com"}).Once()
277 324
@@ -291,7 +338,12 @@ func TestHandler_AttachmentsByImageGet_MultipleTargets(t *testing.T) {
291 containerSvc := inmocks.NewMockContainerService(t)338 containerSvc := inmocks.NewMockContainerService(t)
292 secretSvc := inmocks.NewMockSecretService(t)339 secretSvc := inmocks.NewMockSecretService(t)
293 340
294- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)341+ handler := newTestHandler(t, func(d *HandlerDeps) {
342+ d.ConfigSvc = configSvc
343+ d.AuthSvc = authSvc
344+ d.ContainerSvc = containerSvc
345+ d.SecretSvc = secretSvc
346+ })
295 347
296 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "postgres").Return([]string{"app.example.com", "workers"}).Once()348 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "postgres").Return([]string{"app.example.com", "workers"}).Once()
297 349
@@ -311,7 +363,12 @@ func TestHandler_AttachmentsByImageGet_URLDecodedImage(t *testing.T) {
311 containerSvc := inmocks.NewMockContainerService(t)363 containerSvc := inmocks.NewMockContainerService(t)
312 secretSvc := inmocks.NewMockSecretService(t)364 secretSvc := inmocks.NewMockSecretService(t)
313 365
314- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)366+ handler := newTestHandler(t, func(d *HandlerDeps) {
367+ d.ConfigSvc = configSvc
368+ d.AuthSvc = authSvc
369+ d.ContainerSvc = containerSvc
370+ d.SecretSvc = secretSvc
371+ })
315 372
316 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "registry/org/image:tag").Return([]string{"app.example.com"}).Once()373 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "registry/org/image:tag").Return([]string{"app.example.com"}).Once()
317 374
@@ -331,7 +388,12 @@ func TestHandler_AttachmentsByImageGet_EmptyResultSet(t *testing.T) {
331 containerSvc := inmocks.NewMockContainerService(t)388 containerSvc := inmocks.NewMockContainerService(t)
332 secretSvc := inmocks.NewMockSecretService(t)389 secretSvc := inmocks.NewMockSecretService(t)
333 390
334- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)391+ handler := newTestHandler(t, func(d *HandlerDeps) {
392+ d.ConfigSvc = configSvc
393+ d.AuthSvc = authSvc
394+ d.ContainerSvc = containerSvc
395+ d.SecretSvc = secretSvc
396+ })
335 397
336 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "missing:latest").Return([]string{}).Once()398 configSvc.EXPECT().FindAttachmentTargetsByImage(mock.Anything, "missing:latest").Return([]string{}).Once()
337 399
@@ -351,7 +413,12 @@ func TestHandler_AttachmentsByImageGet_MissingImagePath(t *testing.T) {
351 containerSvc := inmocks.NewMockContainerService(t)413 containerSvc := inmocks.NewMockContainerService(t)
352 secretSvc := inmocks.NewMockSecretService(t)414 secretSvc := inmocks.NewMockSecretService(t)
353 415
354- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)416+ handler := newTestHandler(t, func(d *HandlerDeps) {
417+ d.ConfigSvc = configSvc
418+ d.AuthSvc = authSvc
419+ d.ContainerSvc = containerSvc
420+ d.SecretSvc = secretSvc
421+ })
355 422
356 req := httptest.NewRequest("GET", "/admin/attachments/by-image", nil)423 req := httptest.NewRequest("GET", "/admin/attachments/by-image", nil)
357 req = req.WithContext(ctxWithScopes("admin:config:read"))424 req = req.WithContext(ctxWithScopes("admin:config:read"))
@@ -369,7 +436,12 @@ func TestHandler_AttachmentsByImageGet_InvalidEncoding(t *testing.T) {
369 containerSvc := inmocks.NewMockContainerService(t)436 containerSvc := inmocks.NewMockContainerService(t)
370 secretSvc := inmocks.NewMockSecretService(t)437 secretSvc := inmocks.NewMockSecretService(t)
371 438
372- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)439+ handler := newTestHandler(t, func(d *HandlerDeps) {
440+ d.ConfigSvc = configSvc
441+ d.AuthSvc = authSvc
442+ d.ContainerSvc = containerSvc
443+ d.SecretSvc = secretSvc
444+ })
373 445
374 req := httptest.NewRequest("GET", "/admin/attachments/by-image/placeholder", nil)446 req := httptest.NewRequest("GET", "/admin/attachments/by-image/placeholder", nil)
375 req.URL = &url.URL{Path: "/admin/attachments/by-image/%zz"}447 req.URL = &url.URL{Path: "/admin/attachments/by-image/%zz"}
@@ -388,7 +460,12 @@ func TestHandler_AttachmentsByImageGet_RequiresReadScope(t *testing.T) {
388 containerSvc := inmocks.NewMockContainerService(t)460 containerSvc := inmocks.NewMockContainerService(t)
389 secretSvc := inmocks.NewMockSecretService(t)461 secretSvc := inmocks.NewMockSecretService(t)
390 462
391- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)463+ handler := newTestHandler(t, func(d *HandlerDeps) {
464+ d.ConfigSvc = configSvc
465+ d.AuthSvc = authSvc
466+ d.ContainerSvc = containerSvc
467+ d.SecretSvc = secretSvc
468+ })
392 469
393 req := httptest.NewRequest("GET", "/admin/attachments/by-image/postgres:16", nil)470 req := httptest.NewRequest("GET", "/admin/attachments/by-image/postgres:16", nil)
394 req = req.WithContext(ctxWithScopes("admin:routes:read"))471 req = req.WithContext(ctxWithScopes("admin:routes:read"))
@@ -406,7 +483,12 @@ func TestHandler_RoutesDelete_RequiresWriteScope(t *testing.T) {
406 containerSvc := inmocks.NewMockContainerService(t)483 containerSvc := inmocks.NewMockContainerService(t)
407 secretSvc := inmocks.NewMockSecretService(t)484 secretSvc := inmocks.NewMockSecretService(t)
408 485
409- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)486+ handler := newTestHandler(t, func(d *HandlerDeps) {
487+ d.ConfigSvc = configSvc
488+ d.AuthSvc = authSvc
489+ d.ContainerSvc = containerSvc
490+ d.SecretSvc = secretSvc
491+ })
410 492
411 tests := []struct {493 tests := []struct {
412 name string494 name string
@@ -450,7 +532,12 @@ func TestHandler_SecretsGet_RequiresReadScope(t *testing.T) {
450 containerSvc := inmocks.NewMockContainerService(t)532 containerSvc := inmocks.NewMockContainerService(t)
451 secretSvc := inmocks.NewMockSecretService(t)533 secretSvc := inmocks.NewMockSecretService(t)
452 534
453- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)535+ handler := newTestHandler(t, func(d *HandlerDeps) {
536+ d.ConfigSvc = configSvc
537+ d.AuthSvc = authSvc
538+ d.ContainerSvc = containerSvc
539+ d.SecretSvc = secretSvc
540+ })
454 541
455 tests := []struct {542 tests := []struct {
456 name string543 name string
@@ -507,7 +594,12 @@ func TestHandler_SecretsPost_RequiresWriteScope(t *testing.T) {
507 containerSvc := inmocks.NewMockContainerService(t)594 containerSvc := inmocks.NewMockContainerService(t)
508 secretSvc := inmocks.NewMockSecretService(t)595 secretSvc := inmocks.NewMockSecretService(t)
509 596
510- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)597+ handler := newTestHandler(t, func(d *HandlerDeps) {
598+ d.ConfigSvc = configSvc
599+ d.AuthSvc = authSvc
600+ d.ContainerSvc = containerSvc
601+ d.SecretSvc = secretSvc
602+ })
511 603
512 secretsJSON := `{"API_KEY": "secret123"}`604 secretsJSON := `{"API_KEY": "secret123"}`
513 605
@@ -556,7 +648,12 @@ func TestHandler_SecretsDelete_RequiresWriteScope(t *testing.T) {
556 containerSvc := inmocks.NewMockContainerService(t)648 containerSvc := inmocks.NewMockContainerService(t)
557 secretSvc := inmocks.NewMockSecretService(t)649 secretSvc := inmocks.NewMockSecretService(t)
558 650
559- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)651+ handler := newTestHandler(t, func(d *HandlerDeps) {
652+ d.ConfigSvc = configSvc
653+ d.AuthSvc = authSvc
654+ d.ContainerSvc = containerSvc
655+ d.SecretSvc = secretSvc
656+ })
560 657
561 tests := []struct {658 tests := []struct {
562 name string659 name string
@@ -600,7 +697,12 @@ func TestHandler_AttachmentSecretsPost(t *testing.T) {
600 containerSvc := inmocks.NewMockContainerService(t)697 containerSvc := inmocks.NewMockContainerService(t)
601 secretSvc := inmocks.NewMockSecretService(t)698 secretSvc := inmocks.NewMockSecretService(t)
602 699
603- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)700+ handler := newTestHandler(t, func(d *HandlerDeps) {
701+ d.ConfigSvc = configSvc
702+ d.AuthSvc = authSvc
703+ d.ContainerSvc = containerSvc
704+ d.SecretSvc = secretSvc
705+ })
604 706
605 secretSvc.EXPECT().SetAttachment(mock.Anything, "app.example.com", "redis", mock.AnythingOfType("map[string]string")).Return(nil)707 secretSvc.EXPECT().SetAttachment(mock.Anything, "app.example.com", "redis", mock.AnythingOfType("map[string]string")).Return(nil)
606 708
@@ -626,7 +728,12 @@ func TestHandler_AttachmentSecretsDelete(t *testing.T) {
626 containerSvc := inmocks.NewMockContainerService(t)728 containerSvc := inmocks.NewMockContainerService(t)
627 secretSvc := inmocks.NewMockSecretService(t)729 secretSvc := inmocks.NewMockSecretService(t)
628 730
629- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)731+ handler := newTestHandler(t, func(d *HandlerDeps) {
732+ d.ConfigSvc = configSvc
733+ d.AuthSvc = authSvc
734+ d.ContainerSvc = containerSvc
735+ d.SecretSvc = secretSvc
736+ })
630 737
631 secretSvc.EXPECT().DeleteAttachment(mock.Anything, "app.example.com", "redis", "REDIS_URL").Return(nil)738 secretSvc.EXPECT().DeleteAttachment(mock.Anything, "app.example.com", "redis", "REDIS_URL").Return(nil)
632 739
@@ -652,7 +759,13 @@ func TestHandler_Bootstrap_HappyPath(t *testing.T) {
652 secretSvc := inmocks.NewMockSecretService(t)759 secretSvc := inmocks.NewMockSecretService(t)
653 registrySvc := inmocks.NewMockRegistryService(t)760 registrySvc := inmocks.NewMockRegistryService(t)
654 761
655- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)762+ handler := newTestHandler(t, func(d *HandlerDeps) {
763+ d.ConfigSvc = configSvc
764+ d.AuthSvc = authSvc
765+ d.ContainerSvc = containerSvc
766+ d.SecretSvc = secretSvc
767+ d.RegistrySvc = registrySvc
768+ })
656 769
657 configSvc.EXPECT().GetRegistryDomain().Return("reg.bnema.dev").Once()770 configSvc.EXPECT().GetRegistryDomain().Return("reg.bnema.dev").Once()
658 configSvc.EXPECT().AddRoute(mock.Anything, domain.Route{Domain: "app.example.com", Image: "reg.bnema.dev/myapp"}).Return(nil).Once()771 configSvc.EXPECT().AddRoute(mock.Anything, domain.Route{Domain: "app.example.com", Image: "reg.bnema.dev/myapp"}).Return(nil).Once()
@@ -699,7 +812,12 @@ func TestHandler_Bootstrap_RequiresPermissions(t *testing.T) {
699 containerSvc := inmocks.NewMockContainerService(t)812 containerSvc := inmocks.NewMockContainerService(t)
700 secretSvc := inmocks.NewMockSecretService(t)813 secretSvc := inmocks.NewMockSecretService(t)
701 814
702- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)815+ handler := newTestHandler(t, func(d *HandlerDeps) {
816+ d.ConfigSvc = configSvc
817+ d.AuthSvc = authSvc
818+ d.ContainerSvc = containerSvc
819+ d.SecretSvc = secretSvc
820+ })
703 821
704 tests := []struct {822 tests := []struct {
705 name string823 name string
@@ -741,7 +859,12 @@ func TestHandler_Status_RequiresReadScope(t *testing.T) {
741 containerSvc := inmocks.NewMockContainerService(t)859 containerSvc := inmocks.NewMockContainerService(t)
742 secretSvc := inmocks.NewMockSecretService(t)860 secretSvc := inmocks.NewMockSecretService(t)
743 861
744- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)862+ handler := newTestHandler(t, func(d *HandlerDeps) {
863+ d.ConfigSvc = configSvc
864+ d.AuthSvc = authSvc
865+ d.ContainerSvc = containerSvc
866+ d.SecretSvc = secretSvc
867+ })
745 868
746 tests := []struct {869 tests := []struct {
747 name string870 name string
@@ -795,7 +918,12 @@ func TestHandler_Config_RequiresReadScope(t *testing.T) {
795 containerSvc := inmocks.NewMockContainerService(t)918 containerSvc := inmocks.NewMockContainerService(t)
796 secretSvc := inmocks.NewMockSecretService(t)919 secretSvc := inmocks.NewMockSecretService(t)
797 920
798- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)921+ handler := newTestHandler(t, func(d *HandlerDeps) {
922+ d.ConfigSvc = configSvc
923+ d.AuthSvc = authSvc
924+ d.ContainerSvc = containerSvc
925+ d.SecretSvc = secretSvc
926+ })
799 927
800 tests := []struct {928 tests := []struct {
801 name string929 name string
@@ -852,7 +980,12 @@ func TestHandler_Reload_RequiresWriteScope(t *testing.T) {
852 containerSvc := inmocks.NewMockContainerService(t)980 containerSvc := inmocks.NewMockContainerService(t)
853 secretSvc := inmocks.NewMockSecretService(t)981 secretSvc := inmocks.NewMockSecretService(t)
854 982
855- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)983+ handler := newTestHandler(t, func(d *HandlerDeps) {
984+ d.ConfigSvc = configSvc
985+ d.AuthSvc = authSvc
986+ d.ContainerSvc = containerSvc
987+ d.SecretSvc = secretSvc
988+ })
856 989
857 tests := []struct {990 tests := []struct {
858 name string991 name string
@@ -906,7 +1039,12 @@ func TestHandler_RoutesGet_ReturnsRoutes(t *testing.T) {
906 containerSvc := inmocks.NewMockContainerService(t)1039 containerSvc := inmocks.NewMockContainerService(t)
907 secretSvc := inmocks.NewMockSecretService(t)1040 secretSvc := inmocks.NewMockSecretService(t)
908 1041
909- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1042+ handler := newTestHandler(t, func(d *HandlerDeps) {
1043+ d.ConfigSvc = configSvc
1044+ d.AuthSvc = authSvc
1045+ d.ContainerSvc = containerSvc
1046+ d.SecretSvc = secretSvc
1047+ })
910 1048
911 expectedRoutes := []domain.Route{1049 expectedRoutes := []domain.Route{
912 {Domain: "app1.example.com", Image: "app1:latest"},1050 {Domain: "app1.example.com", Image: "app1:latest"},
@@ -941,7 +1079,12 @@ func TestHandler_RoutesGet_SingleRoute(t *testing.T) {
941 containerSvc := inmocks.NewMockContainerService(t)1079 containerSvc := inmocks.NewMockContainerService(t)
942 secretSvc := inmocks.NewMockSecretService(t)1080 secretSvc := inmocks.NewMockSecretService(t)
943 1081
944- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1082+ handler := newTestHandler(t, func(d *HandlerDeps) {
1083+ d.ConfigSvc = configSvc
1084+ d.AuthSvc = authSvc
1085+ d.ContainerSvc = containerSvc
1086+ d.SecretSvc = secretSvc
1087+ })
945 1088
946 configSvc.EXPECT().GetRoute(mock.Anything, "app.example.com").Return(&domain.Route{1089 configSvc.EXPECT().GetRoute(mock.Anything, "app.example.com").Return(&domain.Route{
947 Domain: "app.example.com", Image: "app:latest",1090 Domain: "app.example.com", Image: "app:latest",
@@ -972,7 +1115,12 @@ func TestHandler_RoutesGet_NotFound(t *testing.T) {
972 containerSvc := inmocks.NewMockContainerService(t)1115 containerSvc := inmocks.NewMockContainerService(t)
973 secretSvc := inmocks.NewMockSecretService(t)1116 secretSvc := inmocks.NewMockSecretService(t)
974 1117
975- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1118+ handler := newTestHandler(t, func(d *HandlerDeps) {
1119+ d.ConfigSvc = configSvc
1120+ d.AuthSvc = authSvc
1121+ d.ContainerSvc = containerSvc
1122+ d.SecretSvc = secretSvc
1123+ })
976 1124
977 configSvc.EXPECT().GetRoute(mock.Anything, "nonexistent.example.com").Return(nil, domain.ErrRouteNotFound)1125 configSvc.EXPECT().GetRoute(mock.Anything, "nonexistent.example.com").Return(nil, domain.ErrRouteNotFound)
978 1126
@@ -1016,7 +1164,13 @@ func TestHandler_RoutesPost_MissingFields(t *testing.T) {
1016 secretSvc := inmocks.NewMockSecretService(t)1164 secretSvc := inmocks.NewMockSecretService(t)
1017 registrySvc := inmocks.NewMockRegistryService(t)1165 registrySvc := inmocks.NewMockRegistryService(t)
1018 1166
1019- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)1167+ handler := newTestHandler(t, func(d *HandlerDeps) {
1168+ d.ConfigSvc = configSvc
1169+ d.AuthSvc = authSvc
1170+ d.ContainerSvc = containerSvc
1171+ d.SecretSvc = secretSvc
1172+ d.RegistrySvc = registrySvc
1173+ })
1020 1174
1021 configSvc.EXPECT().AddRoute(mock.Anything, mock.Anything).Return(tt.mockErr)1175 configSvc.EXPECT().AddRoute(mock.Anything, mock.Anything).Return(tt.mockErr)
1022 1176
@@ -1037,7 +1191,12 @@ func TestHandler_RoutesPut_MissingDomain(t *testing.T) {
1037 containerSvc := inmocks.NewMockContainerService(t)1191 containerSvc := inmocks.NewMockContainerService(t)
1038 secretSvc := inmocks.NewMockSecretService(t)1192 secretSvc := inmocks.NewMockSecretService(t)
1039 1193
1040- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1194+ handler := newTestHandler(t, func(d *HandlerDeps) {
1195+ d.ConfigSvc = configSvc
1196+ d.AuthSvc = authSvc
1197+ d.ContainerSvc = containerSvc
1198+ d.SecretSvc = secretSvc
1199+ })
1041 1200
1042 req := httptest.NewRequest("PUT", "/admin/routes/", bytes.NewBufferString(`{"image": "app:latest"}`))1201 req := httptest.NewRequest("PUT", "/admin/routes/", bytes.NewBufferString(`{"image": "app:latest"}`))
1043 req = req.WithContext(ctxWithScopes("admin:routes:write"))1202 req = req.WithContext(ctxWithScopes("admin:routes:write"))
@@ -1054,7 +1213,12 @@ func TestHandler_RoutesDelete_MissingDomain(t *testing.T) {
1054 containerSvc := inmocks.NewMockContainerService(t)1213 containerSvc := inmocks.NewMockContainerService(t)
1055 secretSvc := inmocks.NewMockSecretService(t)1214 secretSvc := inmocks.NewMockSecretService(t)
1056 1215
1057- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1216+ handler := newTestHandler(t, func(d *HandlerDeps) {
1217+ d.ConfigSvc = configSvc
1218+ d.AuthSvc = authSvc
1219+ d.ContainerSvc = containerSvc
1220+ d.SecretSvc = secretSvc
1221+ })
1058 1222
1059 req := httptest.NewRequest("DELETE", "/admin/routes/", nil)1223 req := httptest.NewRequest("DELETE", "/admin/routes/", nil)
1060 req = req.WithContext(ctxWithScopes("admin:routes:write"))1224 req = req.WithContext(ctxWithScopes("admin:routes:write"))
@@ -1071,7 +1235,12 @@ func TestHandler_Secrets_MissingDomain(t *testing.T) {
1071 containerSvc := inmocks.NewMockContainerService(t)1235 containerSvc := inmocks.NewMockContainerService(t)
1072 secretSvc := inmocks.NewMockSecretService(t)1236 secretSvc := inmocks.NewMockSecretService(t)
1073 1237
1074- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1238+ handler := newTestHandler(t, func(d *HandlerDeps) {
1239+ d.ConfigSvc = configSvc
1240+ d.AuthSvc = authSvc
1241+ d.ContainerSvc = containerSvc
1242+ d.SecretSvc = secretSvc
1243+ })
1075 1244
1076 req := httptest.NewRequest("GET", "/admin/secrets/", nil)1245 req := httptest.NewRequest("GET", "/admin/secrets/", nil)
1077 req = req.WithContext(ctxWithScopes("admin:secrets:read"))1246 req = req.WithContext(ctxWithScopes("admin:secrets:read"))
@@ -1088,7 +1257,12 @@ func TestHandler_SecretsDelete_MissingKey(t *testing.T) {
1088 containerSvc := inmocks.NewMockContainerService(t)1257 containerSvc := inmocks.NewMockContainerService(t)
1089 secretSvc := inmocks.NewMockSecretService(t)1258 secretSvc := inmocks.NewMockSecretService(t)
1090 1259
1091- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1260+ handler := newTestHandler(t, func(d *HandlerDeps) {
1261+ d.ConfigSvc = configSvc
1262+ d.AuthSvc = authSvc
1263+ d.ContainerSvc = containerSvc
1264+ d.SecretSvc = secretSvc
1265+ })
1092 1266
1093 req := httptest.NewRequest("DELETE", "/admin/secrets/app.example.com", nil)1267 req := httptest.NewRequest("DELETE", "/admin/secrets/app.example.com", nil)
1094 req = req.WithContext(ctxWithScopes("admin:secrets:write"))1268 req = req.WithContext(ctxWithScopes("admin:secrets:write"))
@@ -1105,7 +1279,12 @@ func TestHandler_MethodNotAllowed(t *testing.T) {
1105 containerSvc := inmocks.NewMockContainerService(t)1279 containerSvc := inmocks.NewMockContainerService(t)
1106 secretSvc := inmocks.NewMockSecretService(t)1280 secretSvc := inmocks.NewMockSecretService(t)
1107 1281
1108- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1282+ handler := newTestHandler(t, func(d *HandlerDeps) {
1283+ d.ConfigSvc = configSvc
1284+ d.AuthSvc = authSvc
1285+ d.ContainerSvc = containerSvc
1286+ d.SecretSvc = secretSvc
1287+ })
1109 1288
1110 tests := []struct {1289 tests := []struct {
1111 method string1290 method string
@@ -1138,7 +1317,12 @@ func TestHandler_Restart_InvalidDomain(t *testing.T) {
1138 containerSvc := inmocks.NewMockContainerService(t)1317 containerSvc := inmocks.NewMockContainerService(t)
1139 secretSvc := inmocks.NewMockSecretService(t)1318 secretSvc := inmocks.NewMockSecretService(t)
1140 1319
1141- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1320+ handler := newTestHandler(t, func(d *HandlerDeps) {
1321+ d.ConfigSvc = configSvc
1322+ d.AuthSvc = authSvc
1323+ d.ContainerSvc = containerSvc
1324+ d.SecretSvc = secretSvc
1325+ })
1142 1326
1143 tests := []struct {1327 tests := []struct {
1144 name string1328 name string
@@ -1168,7 +1352,12 @@ func TestHandler_Restart_ContainerNotFound(t *testing.T) {
1168 containerSvc := inmocks.NewMockContainerService(t)1352 containerSvc := inmocks.NewMockContainerService(t)
1169 secretSvc := inmocks.NewMockSecretService(t)1353 secretSvc := inmocks.NewMockSecretService(t)
1170 1354
1171- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1355+ handler := newTestHandler(t, func(d *HandlerDeps) {
1356+ d.ConfigSvc = configSvc
1357+ d.AuthSvc = authSvc
1358+ d.ContainerSvc = containerSvc
1359+ d.SecretSvc = secretSvc
1360+ })
1172 1361
1173 containerSvc.EXPECT().Restart(mock.Anything, "nonexistent.example.com", false).Return(domain.ErrContainerNotFound)1362 containerSvc.EXPECT().Restart(mock.Anything, "nonexistent.example.com", false).Return(domain.ErrContainerNotFound)
1174 1363
@@ -1187,7 +1376,12 @@ func TestHandler_Restart_GenericError(t *testing.T) {
1187 containerSvc := inmocks.NewMockContainerService(t)1376 containerSvc := inmocks.NewMockContainerService(t)
1188 secretSvc := inmocks.NewMockSecretService(t)1377 secretSvc := inmocks.NewMockSecretService(t)
1189 1378
1190- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1379+ handler := newTestHandler(t, func(d *HandlerDeps) {
1380+ d.ConfigSvc = configSvc
1381+ d.AuthSvc = authSvc
1382+ d.ContainerSvc = containerSvc
1383+ d.SecretSvc = secretSvc
1384+ })
1191 1385
1192 containerSvc.EXPECT().Restart(mock.Anything, "test.example.com", false).Return(fmt.Errorf("docker daemon error: secret details"))1386 containerSvc.EXPECT().Restart(mock.Anything, "test.example.com", false).Return(fmt.Errorf("docker daemon error: secret details"))
1193 1387
@@ -1209,7 +1403,12 @@ func TestHandler_Restart_Success(t *testing.T) {
1209 containerSvc := inmocks.NewMockContainerService(t)1403 containerSvc := inmocks.NewMockContainerService(t)
1210 secretSvc := inmocks.NewMockSecretService(t)1404 secretSvc := inmocks.NewMockSecretService(t)
1211 1405
1212- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1406+ handler := newTestHandler(t, func(d *HandlerDeps) {
1407+ d.ConfigSvc = configSvc
1408+ d.AuthSvc = authSvc
1409+ d.ContainerSvc = containerSvc
1410+ d.SecretSvc = secretSvc
1411+ })
1213 1412
1214 tests := []struct {1413 tests := []struct {
1215 name string1414 name string
@@ -1256,7 +1455,12 @@ func TestHandler_Tags_InvalidRepository(t *testing.T) {
1256 containerSvc := inmocks.NewMockContainerService(t)1455 containerSvc := inmocks.NewMockContainerService(t)
1257 secretSvc := inmocks.NewMockSecretService(t)1456 secretSvc := inmocks.NewMockSecretService(t)
1258 1457
1259- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1458+ handler := newTestHandler(t, func(d *HandlerDeps) {
1459+ d.ConfigSvc = configSvc
1460+ d.AuthSvc = authSvc
1461+ d.ContainerSvc = containerSvc
1462+ d.SecretSvc = secretSvc
1463+ })
1260 1464
1261 tests := []struct {1465 tests := []struct {
1262 name string1466 name string
@@ -1287,7 +1491,13 @@ func TestHandler_Tags_Success(t *testing.T) {
1287 secretSvc := inmocks.NewMockSecretService(t)1491 secretSvc := inmocks.NewMockSecretService(t)
1288 registrySvc := inmocks.NewMockRegistryService(t)1492 registrySvc := inmocks.NewMockRegistryService(t)
1289 1493
1290- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)1494+ handler := newTestHandler(t, func(d *HandlerDeps) {
1495+ d.ConfigSvc = configSvc
1496+ d.AuthSvc = authSvc
1497+ d.ContainerSvc = containerSvc
1498+ d.SecretSvc = secretSvc
1499+ d.RegistrySvc = registrySvc
1500+ })
1291 1501
1292 registrySvc.EXPECT().ListTags(mock.Anything, "myapp").Return([]string{"latest", "v1.0"}, nil)1502 registrySvc.EXPECT().ListTags(mock.Anything, "myapp").Return([]string{"latest", "v1.0"}, nil)
1293 1503
@@ -1314,7 +1524,13 @@ func TestHandler_Tags_Error(t *testing.T) {
1314 secretSvc := inmocks.NewMockSecretService(t)1524 secretSvc := inmocks.NewMockSecretService(t)
1315 registrySvc := inmocks.NewMockRegistryService(t)1525 registrySvc := inmocks.NewMockRegistryService(t)
1316 1526
1317- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, registrySvc, nil, testLogger(), nil, nil)1527+ handler := newTestHandler(t, func(d *HandlerDeps) {
1528+ d.ConfigSvc = configSvc
1529+ d.AuthSvc = authSvc
1530+ d.ContainerSvc = containerSvc
1531+ d.SecretSvc = secretSvc
1532+ d.RegistrySvc = registrySvc
1533+ })
1318 1534
1319 registrySvc.EXPECT().ListTags(mock.Anything, "myapp").Return(nil, fmt.Errorf("registry error"))1535 registrySvc.EXPECT().ListTags(mock.Anything, "myapp").Return(nil, fmt.Errorf("registry error"))
1320 1536
@@ -1334,7 +1550,12 @@ func TestHandler_AttachmentSecretsPost_RequiresWriteScope(t *testing.T) {
1334 containerSvc := inmocks.NewMockContainerService(t)1550 containerSvc := inmocks.NewMockContainerService(t)
1335 secretSvc := inmocks.NewMockSecretService(t)1551 secretSvc := inmocks.NewMockSecretService(t)
1336 1552
1337- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1553+ handler := newTestHandler(t, func(d *HandlerDeps) {
1554+ d.ConfigSvc = configSvc
1555+ d.AuthSvc = authSvc
1556+ d.ContainerSvc = containerSvc
1557+ d.SecretSvc = secretSvc
1558+ })
1338 1559
1339 body := `{"REDIS_URL": "redis://localhost:6379"}`1560 body := `{"REDIS_URL": "redis://localhost:6379"}`
1340 req := httptest.NewRequest("POST", "/admin/secrets/app.example.com/attachments/redis", bytes.NewBufferString(body))1561 req := httptest.NewRequest("POST", "/admin/secrets/app.example.com/attachments/redis", bytes.NewBufferString(body))
@@ -1352,7 +1573,12 @@ func TestHandler_AttachmentSecretsDelete_RequiresWriteScope(t *testing.T) {
1352 containerSvc := inmocks.NewMockContainerService(t)1573 containerSvc := inmocks.NewMockContainerService(t)
1353 secretSvc := inmocks.NewMockSecretService(t)1574 secretSvc := inmocks.NewMockSecretService(t)
1354 1575
1355- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1576+ handler := newTestHandler(t, func(d *HandlerDeps) {
1577+ d.ConfigSvc = configSvc
1578+ d.AuthSvc = authSvc
1579+ d.ContainerSvc = containerSvc
1580+ d.SecretSvc = secretSvc
1581+ })
1356 1582
1357 req := httptest.NewRequest("DELETE", "/admin/secrets/app.example.com/attachments/redis/REDIS_URL", nil)1583 req := httptest.NewRequest("DELETE", "/admin/secrets/app.example.com/attachments/redis/REDIS_URL", nil)
1358 req = req.WithContext(ctxWithScopes("admin:secrets:read"))1584 req = req.WithContext(ctxWithScopes("admin:secrets:read"))
@@ -1369,7 +1595,12 @@ func TestHandler_AttachmentSecretsDelete_RequiresKey(t *testing.T) {
1369 containerSvc := inmocks.NewMockContainerService(t)1595 containerSvc := inmocks.NewMockContainerService(t)
1370 secretSvc := inmocks.NewMockSecretService(t)1596 secretSvc := inmocks.NewMockSecretService(t)
1371 1597
1372- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1598+ handler := newTestHandler(t, func(d *HandlerDeps) {
1599+ d.ConfigSvc = configSvc
1600+ d.AuthSvc = authSvc
1601+ d.ContainerSvc = containerSvc
1602+ d.SecretSvc = secretSvc
1603+ })
1373 1604
1374 req := httptest.NewRequest("DELETE", "/admin/secrets/app.example.com/attachments/redis", nil)1605 req := httptest.NewRequest("DELETE", "/admin/secrets/app.example.com/attachments/redis", nil)
1375 req = req.WithContext(ctxWithScopes("admin:secrets:write"))1606 req = req.WithContext(ctxWithScopes("admin:secrets:write"))
@@ -1387,7 +1618,12 @@ func TestHandler_AttachmentSecretsPost_InvalidJSON(t *testing.T) {
1387 containerSvc := inmocks.NewMockContainerService(t)1618 containerSvc := inmocks.NewMockContainerService(t)
1388 secretSvc := inmocks.NewMockSecretService(t)1619 secretSvc := inmocks.NewMockSecretService(t)
1389 1620
1390- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1621+ handler := newTestHandler(t, func(d *HandlerDeps) {
1622+ d.ConfigSvc = configSvc
1623+ d.AuthSvc = authSvc
1624+ d.ContainerSvc = containerSvc
1625+ d.SecretSvc = secretSvc
1626+ })
1391 1627
1392 body := `{not valid json`1628 body := `{not valid json`
1393 req := httptest.NewRequest("POST", "/admin/secrets/app.example.com/attachments/redis", bytes.NewBufferString(body))1629 req := httptest.NewRequest("POST", "/admin/secrets/app.example.com/attachments/redis", bytes.NewBufferString(body))
@@ -1406,7 +1642,12 @@ func TestHandler_NotFound(t *testing.T) {
1406 containerSvc := inmocks.NewMockContainerService(t)1642 containerSvc := inmocks.NewMockContainerService(t)
1407 secretSvc := inmocks.NewMockSecretService(t)1643 secretSvc := inmocks.NewMockSecretService(t)
1408 1644
1409- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)1645+ handler := newTestHandler(t, func(d *HandlerDeps) {
1646+ d.ConfigSvc = configSvc
1647+ d.AuthSvc = authSvc
1648+ d.ContainerSvc = containerSvc
1649+ d.SecretSvc = secretSvc
1650+ })
1410 1651
1411 req := httptest.NewRequest("GET", "/admin/unknown", nil)1652 req := httptest.NewRequest("GET", "/admin/unknown", nil)
1412 req = req.WithContext(ctxWithScopes("admin:*:*"))1653 req = req.WithContext(ctxWithScopes("admin:*:*"))
@@ -1426,7 +1667,13 @@ func TestHandler_BackupsStatus(t *testing.T) {
1426 1667
1427 backupSvc.EXPECT().Status(mock.Anything).Return([]domain.BackupJob{{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted, FilePath: "/var/lib/gordon/backups/private.bak"}}, nil)1668 backupSvc.EXPECT().Status(mock.Anything).Return([]domain.BackupJob{{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted, FilePath: "/var/lib/gordon/backups/private.bak"}}, nil)
1428 1669
1429- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), backupSvc, nil)1670+ handler := newTestHandler(t, func(d *HandlerDeps) {
1671+ d.ConfigSvc = configSvc
1672+ d.AuthSvc = authSvc
1673+ d.ContainerSvc = containerSvc
1674+ d.SecretSvc = secretSvc
1675+ d.BackupSvc = backupSvc
1676+ })
1430 1677
1431 req := httptest.NewRequest("GET", "/admin/backups/status", nil)1678 req := httptest.NewRequest("GET", "/admin/backups/status", nil)
1432 req = req.WithContext(ctxWithScopes("admin:status:read"))1679 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1448,7 +1695,13 @@ func TestHandler_BackupsListDomain(t *testing.T) {
1448 1695
1449 backupSvc.EXPECT().ListBackups(mock.Anything, "app.example.com").Return([]domain.BackupJob{{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted}}, nil)1696 backupSvc.EXPECT().ListBackups(mock.Anything, "app.example.com").Return([]domain.BackupJob{{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted}}, nil)
1450 1697
1451- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), backupSvc, nil)1698+ handler := newTestHandler(t, func(d *HandlerDeps) {
1699+ d.ConfigSvc = configSvc
1700+ d.AuthSvc = authSvc
1701+ d.ContainerSvc = containerSvc
1702+ d.SecretSvc = secretSvc
1703+ d.BackupSvc = backupSvc
1704+ })
1452 1705
1453 req := httptest.NewRequest("GET", "/admin/backups/app.example.com", nil)1706 req := httptest.NewRequest("GET", "/admin/backups/app.example.com", nil)
1454 req = req.WithContext(ctxWithScopes("admin:status:read"))1707 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1469,7 +1722,13 @@ func TestHandler_BackupsRunDomain(t *testing.T) {
1469 1722
1470 backupSvc.EXPECT().RunBackup(mock.Anything, "app.example.com", "postgres").Return(&domain.BackupResult{Job: domain.BackupJob{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted}}, nil)1723 backupSvc.EXPECT().RunBackup(mock.Anything, "app.example.com", "postgres").Return(&domain.BackupResult{Job: domain.BackupJob{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted}}, nil)
1471 1724
1472- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), backupSvc, nil)1725+ handler := newTestHandler(t, func(d *HandlerDeps) {
1726+ d.ConfigSvc = configSvc
1727+ d.AuthSvc = authSvc
1728+ d.ContainerSvc = containerSvc
1729+ d.SecretSvc = secretSvc
1730+ d.BackupSvc = backupSvc
1731+ })
1473 1732
1474 body := bytes.NewBufferString(`{"db":"postgres"}`)1733 body := bytes.NewBufferString(`{"db":"postgres"}`)
1475 req := httptest.NewRequest("POST", "/admin/backups/app.example.com", body)1734 req := httptest.NewRequest("POST", "/admin/backups/app.example.com", body)
@@ -1491,7 +1750,13 @@ func TestHandler_BackupsRunDomain_ChunkedBodyIsDecoded(t *testing.T) {
1491 1750
1492 backupSvc.EXPECT().RunBackup(mock.Anything, "app.example.com", "postgres").Return(&domain.BackupResult{Job: domain.BackupJob{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted}}, nil)1751 backupSvc.EXPECT().RunBackup(mock.Anything, "app.example.com", "postgres").Return(&domain.BackupResult{Job: domain.BackupJob{Domain: "app.example.com", DBName: "postgres", Status: domain.BackupStatusCompleted}}, nil)
1493 1752
1494- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), backupSvc, nil)1753+ handler := newTestHandler(t, func(d *HandlerDeps) {
1754+ d.ConfigSvc = configSvc
1755+ d.AuthSvc = authSvc
1756+ d.ContainerSvc = containerSvc
1757+ d.SecretSvc = secretSvc
1758+ d.BackupSvc = backupSvc
1759+ })
1495 1760
1496 body := bytes.NewBufferString(`{"db":"postgres"}`)1761 body := bytes.NewBufferString(`{"db":"postgres"}`)
1497 req := httptest.NewRequest("POST", "/admin/backups/app.example.com", body)1762 req := httptest.NewRequest("POST", "/admin/backups/app.example.com", body)
@@ -1514,7 +1779,13 @@ func TestHandler_BackupsDetectDomain(t *testing.T) {
1514 1779
1515 backupSvc.EXPECT().DetectDatabases(mock.Anything, "app.example.com").Return([]domain.DBInfo{{Type: domain.DBTypePostgreSQL, Name: "postgres", Port: 5432}}, nil)1780 backupSvc.EXPECT().DetectDatabases(mock.Anything, "app.example.com").Return([]domain.DBInfo{{Type: domain.DBTypePostgreSQL, Name: "postgres", Port: 5432}}, nil)
1516 1781
1517- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), backupSvc, nil)1782+ handler := newTestHandler(t, func(d *HandlerDeps) {
1783+ d.ConfigSvc = configSvc
1784+ d.AuthSvc = authSvc
1785+ d.ContainerSvc = containerSvc
1786+ d.SecretSvc = secretSvc
1787+ d.BackupSvc = backupSvc
1788+ })
1518 1789
1519 req := httptest.NewRequest("GET", "/admin/backups/app.example.com/detect", nil)1790 req := httptest.NewRequest("GET", "/admin/backups/app.example.com/detect", nil)
1520 req = req.WithContext(ctxWithScopes("admin:status:read"))1791 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1556,7 +1827,13 @@ func TestHandler_ImagesGet_ReturnsMappedList(t *testing.T) {
1556 },1827 },
1557 }1828 }
1558 1829
1559- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, imageSvc)1830+ handler := newTestHandler(t, func(d *HandlerDeps) {
1831+ d.ConfigSvc = configSvc
1832+ d.AuthSvc = authSvc
1833+ d.ContainerSvc = containerSvc
1834+ d.SecretSvc = secretSvc
1835+ d.ImageSvc = imageSvc
1836+ })
1560 1837
1561 req := httptest.NewRequest("GET", "/admin/images", nil)1838 req := httptest.NewRequest("GET", "/admin/images", nil)
1562 req = req.WithContext(ctxWithScopes("admin:status:read"))1839 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1617,7 +1894,13 @@ func TestHandler_ImagesPrune_AcceptsOptionalKeepLast(t *testing.T) {
1617 },1894 },
1618 }1895 }
1619 1896
1620- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, imageSvc)1897+ handler := newTestHandler(t, func(d *HandlerDeps) {
1898+ d.ConfigSvc = configSvc
1899+ d.AuthSvc = authSvc
1900+ d.ContainerSvc = containerSvc
1901+ d.SecretSvc = secretSvc
1902+ d.ImageSvc = imageSvc
1903+ })
1621 1904
1622 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(tt.body))1905 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(tt.body))
1623 req = req.WithContext(ctxWithScopes("admin:config:write"))1906 req = req.WithContext(ctxWithScopes("admin:config:write"))
@@ -1646,7 +1929,13 @@ func TestHandler_Images_ErrorMappingForServiceFailures(t *testing.T) {
1646 },1929 },
1647 }1930 }
1648 1931
1649- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, imageSvc)1932+ handler := newTestHandler(t, func(d *HandlerDeps) {
1933+ d.ConfigSvc = configSvc
1934+ d.AuthSvc = authSvc
1935+ d.ContainerSvc = containerSvc
1936+ d.SecretSvc = secretSvc
1937+ d.ImageSvc = imageSvc
1938+ })
1650 1939
1651 req := httptest.NewRequest("GET", "/admin/images", nil)1940 req := httptest.NewRequest("GET", "/admin/images", nil)
1652 req = req.WithContext(ctxWithScopes("admin:status:read"))1941 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1671,7 +1960,13 @@ func TestHandler_Images_ErrorMappingForServiceFailures(t *testing.T) {
1671 },1960 },
1672 }1961 }
1673 1962
1674- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, imageSvc)1963+ handler := newTestHandler(t, func(d *HandlerDeps) {
1964+ d.ConfigSvc = configSvc
1965+ d.AuthSvc = authSvc
1966+ d.ContainerSvc = containerSvc
1967+ d.SecretSvc = secretSvc
1968+ d.ImageSvc = imageSvc
1969+ })
1675 1970
1676 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(`{"keep_last": 2}`))1971 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(`{"keep_last": 2}`))
1677 req = req.WithContext(ctxWithScopes("admin:config:write"))1972 req = req.WithContext(ctxWithScopes("admin:config:write"))
@@ -1692,7 +1987,13 @@ func TestHandler_ImagesPrune_ValidationAndAvailability(t *testing.T) {
1692 containerSvc := inmocks.NewMockContainerService(t)1987 containerSvc := inmocks.NewMockContainerService(t)
1693 secretSvc := inmocks.NewMockSecretService(t)1988 secretSvc := inmocks.NewMockSecretService(t)
1694 1989
1695- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, &stubImageService{})1990+ handler := newTestHandler(t, func(d *HandlerDeps) {
1991+ d.ConfigSvc = configSvc
1992+ d.AuthSvc = authSvc
1993+ d.ContainerSvc = containerSvc
1994+ d.SecretSvc = secretSvc
1995+ d.ImageSvc = &stubImageService{}
1996+ })
1696 1997
1697 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(`{"keep_last": -1}`))1998 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(`{"keep_last": -1}`))
1698 req = req.WithContext(ctxWithScopes("admin:config:write"))1999 req = req.WithContext(ctxWithScopes("admin:config:write"))
@@ -1710,7 +2011,12 @@ func TestHandler_ImagesPrune_ValidationAndAvailability(t *testing.T) {
1710 containerSvc := inmocks.NewMockContainerService(t)2011 containerSvc := inmocks.NewMockContainerService(t)
1711 secretSvc := inmocks.NewMockSecretService(t)2012 secretSvc := inmocks.NewMockSecretService(t)
1712 2013
1713- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil)2014+ handler := newTestHandler(t, func(d *HandlerDeps) {
2015+ d.ConfigSvc = configSvc
2016+ d.AuthSvc = authSvc
2017+ d.ContainerSvc = containerSvc
2018+ d.SecretSvc = secretSvc
2019+ })
1714 2020
1715 req := httptest.NewRequest("GET", "/admin/images", nil)2021 req := httptest.NewRequest("GET", "/admin/images", nil)
1716 req = req.WithContext(ctxWithScopes("admin:status:read"))2022 req = req.WithContext(ctxWithScopes("admin:status:read"))
@@ -1781,7 +2087,13 @@ func TestHandler_ImagesPrune_ScopeDefaults(t *testing.T) {
1781 },2087 },
1782 }2088 }
1783 2089
1784- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, imageSvc)2090+ handler := newTestHandler(t, func(d *HandlerDeps) {
2091+ d.ConfigSvc = configSvc
2092+ d.AuthSvc = authSvc
2093+ d.ContainerSvc = containerSvc
2094+ d.SecretSvc = secretSvc
2095+ d.ImageSvc = imageSvc
2096+ })
1785 2097
1786 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(tt.body))2098 req := httptest.NewRequest("POST", "/admin/images/prune", bytes.NewBufferString(tt.body))
1787 req = req.WithContext(ctxWithScopes("admin:config:write"))2099 req = req.WithContext(ctxWithScopes("admin:config:write"))
@@ -1806,7 +2118,13 @@ func TestHandler_Images_Authorization(t *testing.T) {
1806 containerSvc := inmocks.NewMockContainerService(t)2118 containerSvc := inmocks.NewMockContainerService(t)
1807 secretSvc := inmocks.NewMockSecretService(t)2119 secretSvc := inmocks.NewMockSecretService(t)
1808 2120
1809- handler := NewHandler(configSvc, authSvc, containerSvc, inmocks.NewMockHealthService(t), secretSvc, nil, inmocks.NewMockRegistryService(t), nil, testLogger(), nil, nil, &stubImageService{})2121+ handler := newTestHandler(t, func(d *HandlerDeps) {
2122+ d.ConfigSvc = configSvc
2123+ d.AuthSvc = authSvc
2124+ d.ContainerSvc = containerSvc
2125+ d.SecretSvc = secretSvc
2126+ d.ImageSvc = &stubImageService{}
2127+ })
1810 2128
1811 t.Run("images list requires status:read", func(t *testing.T) {2129 t.Run("images list requires status:read", func(t *testing.T) {
1812 req := httptest.NewRequest("GET", "/admin/images", nil)2130 req := httptest.NewRequest("GET", "/admin/images", nil)
M internal/app/run.go +15 -14
@@ -567,20 +567,21 @@ func createServices(ctx context.Context, v *viper.Viper, cfg Config, log zerowra
567567 })
568568
569569 // Create admin handler for admin API
570- svc.adminHandler = admin.NewHandler(
571- svc.configSvc,
572- svc.authSvc,
573- svc.containerSvc,
574- svc.healthSvc,
575- svc.secretSvc,
576- svc.logSvc,
577- svc.registrySvc,
578- svc.eventBus,
579- log,
580- svc.backupSvc,
581- svc.previewService,
582- svc.imageSvc,
583- ).WithVolumeService(svc.volumeSvc)
570+ svc.adminHandler = admin.NewHandler(admin.HandlerDeps{
571+ ConfigSvc: svc.configSvc,
572+ AuthSvc: svc.authSvc,
573+ ContainerSvc: svc.containerSvc,
574+ HealthSvc: svc.healthSvc,
575+ SecretSvc: svc.secretSvc,
576+ LogSvc: svc.logSvc,
577+ RegistrySvc: svc.registrySvc,
578+ EventBus: svc.eventBus,
579+ Log: log,
580+ BackupSvc: svc.backupSvc,
581+ PreviewSvc: svc.previewService,
582+ ImageSvc: svc.imageSvc,
583+ VolumeSvc: svc.volumeSvc,
584+ })
584585
585586 return svc, nil
586587 }
@@ -567,20 +567,21 @@ func createServices(ctx context.Context, v *viper.Viper, cfg Config, log zerowra
567 })567 })
568 568
569 // Create admin handler for admin API569 // Create admin handler for admin API
570- svc.adminHandler = admin.NewHandler(570+ svc.adminHandler = admin.NewHandler(admin.HandlerDeps{
571- svc.configSvc,571+ ConfigSvc: svc.configSvc,
572- svc.authSvc,572+ AuthSvc: svc.authSvc,
573- svc.containerSvc,573+ ContainerSvc: svc.containerSvc,
574- svc.healthSvc,574+ HealthSvc: svc.healthSvc,
575- svc.secretSvc,575+ SecretSvc: svc.secretSvc,
576- svc.logSvc,576+ LogSvc: svc.logSvc,
577- svc.registrySvc,577+ RegistrySvc: svc.registrySvc,
578- svc.eventBus,578+ EventBus: svc.eventBus,
579- log,579+ Log: log,
580- svc.backupSvc,580+ BackupSvc: svc.backupSvc,
581- svc.previewService,581+ PreviewSvc: svc.previewService,
582- svc.imageSvc,582+ ImageSvc: svc.imageSvc,
583- ).WithVolumeService(svc.volumeSvc)583+ VolumeSvc: svc.volumeSvc,
584+ })
584 585
585 return svc, nil586 return svc, nil
586 }587 }

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