v2.1.0 0fb867d

service_test.go RawHistoryFolder

1package proxy
2
3import (
4	"context"
5	"testing"
6
7	"github.com/bnema/zerowrap"
8	"github.com/stretchr/testify/assert"
9	"github.com/stretchr/testify/mock"
10
11	inmocks "gordon/internal/boundaries/in/mocks"
12	outmocks "gordon/internal/boundaries/out/mocks"
13	"gordon/internal/domain"
14)
15
16func testContext() context.Context {
17	return zerowrap.WithCtx(context.Background(), zerowrap.Default())
18}
19
20func TestService_GetTarget_FromCache(t *testing.T) {
21	runtime := outmocks.NewMockContainerRuntime(t)
22	containerSvc := inmocks.NewMockContainerService(t)
23	configSvc := inmocks.NewMockConfigService(t)
24
25	config := Config{
26		RegistryDomain: "registry.example.com",
27		RegistryPort:   5000,
28	}
29	svc := NewService(runtime, containerSvc, configSvc, config)
30	ctx := testContext()
31
32	// Pre-populate cache
33	cachedTarget := &domain.ProxyTarget{
34		Host:        "192.168.1.100",
35		Port:        8080,
36		ContainerID: "container-123",
37		Scheme:      "http",
38	}
39	svc.targets["app.example.com"] = cachedTarget
40
41	// No mock calls expected - should return from cache
42
43	result, err := svc.GetTarget(ctx, "app.example.com")
44
45	assert.NoError(t, err)
46	assert.Equal(t, cachedTarget, result)
47}
48
49func TestService_GetTarget_ContainerNotFound(t *testing.T) {
50	runtime := outmocks.NewMockContainerRuntime(t)
51	containerSvc := inmocks.NewMockContainerService(t)
52	configSvc := inmocks.NewMockConfigService(t)
53
54	config := Config{}
55	svc := NewService(runtime, containerSvc, configSvc, config)
56	ctx := testContext()
57
58	containerSvc.EXPECT().Get(mock.Anything, "app.example.com").Return(nil, false)
59
60	result, err := svc.GetTarget(ctx, "app.example.com")
61
62	assert.ErrorIs(t, err, domain.ErrNoTargetAvailable)
63	assert.Nil(t, result)
64}
65
66func TestService_RegisterTarget(t *testing.T) {
67	runtime := outmocks.NewMockContainerRuntime(t)
68	containerSvc := inmocks.NewMockContainerService(t)
69	configSvc := inmocks.NewMockConfigService(t)
70
71	svc := NewService(runtime, containerSvc, configSvc, Config{})
72	ctx := testContext()
73
74	target := &domain.ProxyTarget{
75		Host:        "192.168.1.100",
76		Port:        8080,
77		ContainerID: "container-123",
78		Scheme:      "http",
79	}
80
81	err := svc.RegisterTarget(ctx, "app.example.com", target)
82
83	assert.NoError(t, err)
84
85	// Verify target is cached
86	svc.mu.RLock()
87	cached := svc.targets["app.example.com"]
88	svc.mu.RUnlock()
89
90	assert.Equal(t, target, cached)
91}
92
93func TestService_UnregisterTarget(t *testing.T) {
94	runtime := outmocks.NewMockContainerRuntime(t)
95	containerSvc := inmocks.NewMockContainerService(t)
96	configSvc := inmocks.NewMockConfigService(t)
97
98	svc := NewService(runtime, containerSvc, configSvc, Config{})
99	ctx := testContext()
100
101	// Pre-populate
102	svc.targets["app.example.com"] = &domain.ProxyTarget{
103		Host: "192.168.1.100",
104		Port: 8080,
105	}
106
107	err := svc.UnregisterTarget(ctx, "app.example.com")
108
109	assert.NoError(t, err)
110
111	// Verify target is removed
112	svc.mu.RLock()
113	_, exists := svc.targets["app.example.com"]
114	svc.mu.RUnlock()
115
116	assert.False(t, exists)
117}
118
119func TestService_RefreshTargets(t *testing.T) {
120	runtime := outmocks.NewMockContainerRuntime(t)
121	containerSvc := inmocks.NewMockContainerService(t)
122	configSvc := inmocks.NewMockConfigService(t)
123
124	svc := NewService(runtime, containerSvc, configSvc, Config{})
125	ctx := testContext()
126
127	// Pre-populate with some targets
128	svc.targets["app1.example.com"] = &domain.ProxyTarget{Host: "192.168.1.100"}
129	svc.targets["app2.example.com"] = &domain.ProxyTarget{Host: "192.168.1.101"}
130
131	err := svc.RefreshTargets(ctx)
132
133	assert.NoError(t, err)
134
135	// Verify all targets are cleared
136	svc.mu.RLock()
137	count := len(svc.targets)
138	svc.mu.RUnlock()
139
140	assert.Equal(t, 0, count)
141}
142
143func TestService_UpdateConfig(t *testing.T) {
144	runtime := outmocks.NewMockContainerRuntime(t)
145	containerSvc := inmocks.NewMockContainerService(t)
146	configSvc := inmocks.NewMockConfigService(t)
147
148	svc := NewService(runtime, containerSvc, configSvc, Config{
149		RegistryDomain: "old.registry.com",
150		RegistryPort:   5000,
151	})
152
153	newConfig := Config{
154		RegistryDomain: "new.registry.com",
155		RegistryPort:   5001,
156	}
157
158	svc.UpdateConfig(newConfig)
159
160	assert.Equal(t, "new.registry.com", svc.config.RegistryDomain)
161	assert.Equal(t, 5001, svc.config.RegistryPort)
162}
163
164func TestService_isRunningInContainer(t *testing.T) {
165	runtime := outmocks.NewMockContainerRuntime(t)
166	containerSvc := inmocks.NewMockContainerService(t)
167	configSvc := inmocks.NewMockConfigService(t)
168
169	svc := NewService(runtime, containerSvc, configSvc, Config{})
170
171	// This test just verifies the method doesn't panic
172	// The actual result depends on the environment
173	result := svc.isRunningInContainer()
174	assert.IsType(t, true, result) // Just verify it returns a bool
175}
176
177func TestService_InvalidateTarget(t *testing.T) {
178	runtime := outmocks.NewMockContainerRuntime(t)
179	containerSvc := inmocks.NewMockContainerService(t)
180	configSvc := inmocks.NewMockConfigService(t)
181
182	svc := NewService(runtime, containerSvc, configSvc, Config{})
183	ctx := testContext()
184
185	// Pre-populate cache with target
186	svc.targets["app.example.com"] = &domain.ProxyTarget{
187		Host:        "192.168.1.100",
188		Port:        8080,
189		ContainerID: "old-container",
190	}
191
192	// Invalidate the target
193	svc.InvalidateTarget(ctx, "app.example.com")
194
195	// Verify target is removed from cache
196	svc.mu.RLock()
197	_, exists := svc.targets["app.example.com"]
198	svc.mu.RUnlock()
199
200	assert.False(t, exists, "target should be removed from cache after invalidation")
201}
202
203func TestContainerDeployedHandler_CanHandle(t *testing.T) {
204	handler := NewContainerDeployedHandler(testContext(), nil)
205
206	assert.True(t, handler.CanHandle(domain.EventContainerDeployed))
207	assert.False(t, handler.CanHandle(domain.EventImagePushed))
208	assert.False(t, handler.CanHandle(domain.EventConfigReload))
209}
210
211func TestContainerDeployedHandler_Handle_InvalidatesCache(t *testing.T) {
212	runtime := outmocks.NewMockContainerRuntime(t)
213	containerSvc := inmocks.NewMockContainerService(t)
214	configSvc := inmocks.NewMockConfigService(t)
215
216	svc := NewService(runtime, containerSvc, configSvc, Config{})
217	ctx := testContext()
218
219	// Pre-populate cache
220	svc.targets["app.example.com"] = &domain.ProxyTarget{
221		Host:        "192.168.1.100",
222		Port:        8080,
223		ContainerID: "old-container",
224	}
225
226	// Create handler with service as invalidator
227	handler := NewContainerDeployedHandler(ctx, svc)
228
229	// Simulate container deployed event
230	event := domain.Event{
231		ID:    "event-123",
232		Type:  domain.EventContainerDeployed,
233		Route: "app.example.com",
234		Data: &domain.ContainerEventPayload{
235			ContainerID: "new-container",
236			Domain:      "app.example.com",
237		},
238	}
239
240	err := handler.Handle(event)
241
242	assert.NoError(t, err)
243
244	// Verify target was invalidated
245	svc.mu.RLock()
246	_, exists := svc.targets["app.example.com"]
247	svc.mu.RUnlock()
248
249	assert.False(t, exists, "cache should be invalidated after container deployed event")
250}
251
252func TestContainerDeployedHandler_Handle_NoDomain(t *testing.T) {
253	runtime := outmocks.NewMockContainerRuntime(t)
254	containerSvc := inmocks.NewMockContainerService(t)
255	configSvc := inmocks.NewMockConfigService(t)
256
257	svc := NewService(runtime, containerSvc, configSvc, Config{})
258	ctx := testContext()
259
260	handler := NewContainerDeployedHandler(ctx, svc)
261
262	// Event with no domain
263	event := domain.Event{
264		ID:   "event-123",
265		Type: domain.EventContainerDeployed,
266		Data: &domain.ContainerEventPayload{
267			ContainerID: "new-container",
268		},
269	}
270
271	// Should not error, just skip
272	err := handler.Handle(event)
273	assert.NoError(t, err)
274}

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