1package preview
2
3import (
4 "context"
5 "sync"
6 "testing"
7 "time"
8
9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"
11
12 "github.com/bnema/gordon/internal/domain"
13)
14
15type captureStore struct {
16 mu sync.Mutex
17 previews []domain.PreviewRoute
18 history [][]domain.PreviewRoute
19}
20
21func (s *captureStore) Load(_ context.Context) ([]domain.PreviewRoute, error) {
22 s.mu.Lock()
23 defer s.mu.Unlock()
24 cp := make([]domain.PreviewRoute, len(s.previews))
25 copy(cp, s.previews)
26 return cp, nil
27}
28
29func (s *captureStore) Save(_ context.Context, previews []domain.PreviewRoute) error {
30 s.mu.Lock()
31 defer s.mu.Unlock()
32 s.previews = make([]domain.PreviewRoute, len(previews))
33 copy(s.previews, previews)
34 snapshot := make([]domain.PreviewRoute, len(previews))
35 copy(snapshot, previews)
36 s.history = append(s.history, snapshot)
37 return nil
38}
39
40func (s *captureStore) saves() [][]domain.PreviewRoute {
41 s.mu.Lock()
42 defer s.mu.Unlock()
43 cp := make([][]domain.PreviewRoute, len(s.history))
44 for i := range s.history {
45 cp[i] = make([]domain.PreviewRoute, len(s.history[i]))
46 copy(cp[i], s.history[i])
47 }
48 return cp
49}
50
51type fakeAutoConfigProvider struct {
52 previewConfig domain.PreviewConfig
53 routesByImage map[string][]domain.Route
54 allowedDomains []string
55}
56
57func (f *fakeAutoConfigProvider) IsAutoEnabled() bool { return false }
58func (f *fakeAutoConfigProvider) IsPreviewEnabled() bool { return true }
59func (f *fakeAutoConfigProvider) GetPreviewTagPatterns() []string { return f.previewConfig.TagPatterns }
60func (f *fakeAutoConfigProvider) GetPreviewConfig() domain.PreviewConfig { return f.previewConfig }
61func (f *fakeAutoConfigProvider) GetAllowedDomains() []string { return f.allowedDomains }
62func (f *fakeAutoConfigProvider) FindRoutesByImage(_ context.Context, imageName string) []domain.Route {
63 return f.routesByImage[imageName]
64}
65
66func TestAutoPreviewHandler_CanHandle(t *testing.T) {
67 h := &AutoPreviewHandler{}
68 assert.True(t, h.CanHandle(domain.EventImagePushed))
69 assert.False(t, h.CanHandle(domain.EventConfigReload))
70}
71
72func TestResolveBaseRoutes_ReturnsAllEligibleBases(t *testing.T) {
73 routes := []domain.Route{
74 {Domain: "app-preview-old.example.com", Image: "myapp", HTTPS: true},
75 {Domain: "app.example.com", Image: "myapp", HTTPS: true},
76 {Domain: "alias.example.com", Image: "myapp", HTTPS: false},
77 }
78
79 baseRoutes := resolveBaseRoutes(routes, domain.PreviewConfig{Separator: "-preview-"})
80 assert.ElementsMatch(t, []baseRouteInfo{
81 {Domain: "app.example.com", HTTPS: true},
82 {Domain: "alias.example.com", HTTPS: false},
83 }, baseRoutes)
84}
85
86func TestResolveBaseRoutes_TreatsSeparatorWithoutBaseAsEligible(t *testing.T) {
87 routes := []domain.Route{
88 {Domain: "my--app.example.com", Image: "myapp", HTTPS: false},
89 {Domain: "other.example.com", Image: "myapp", HTTPS: true},
90 }
91
92 baseRoutes := resolveBaseRoutes(routes, domain.PreviewConfig{Separator: "--"})
93 assert.ElementsMatch(t, []baseRouteInfo{
94 {Domain: "my--app.example.com", HTTPS: false},
95 {Domain: "other.example.com", HTTPS: true},
96 }, baseRoutes)
97}
98
99func TestAutoPreviewHandler_Handle_CreatesPreviewPerBaseRoute(t *testing.T) {
100 store := &captureStore{}
101 svc := NewService(store, time.Hour)
102 h := NewAutoPreviewHandler(t.Context(), &fakeAutoConfigProvider{
103 previewConfig: domain.PreviewConfig{
104 Separator: "--",
105 TagPatterns: []string{"preview-*"},
106 TTL: time.Hour,
107 },
108 allowedDomains: []string{"*"},
109 routesByImage: map[string][]domain.Route{
110 "myapp": {
111 {Domain: "app.example.com", Image: "myapp", HTTPS: true},
112 {Domain: "alias.example.com", Image: "myapp", HTTPS: false},
113 },
114 },
115 }, svc)
116
117 err := h.Handle(t.Context(), domain.Event{
118 ID: "evt-1",
119 Type: domain.EventImagePushed,
120 Data: domain.ImagePushedPayload{Name: "myapp", Reference: "preview-login"},
121 })
122 require.NoError(t, err)
123
124 require.Eventually(t, func() bool {
125 for _, save := range store.saves() {
126 if len(save) != 2 {
127 continue
128 }
129
130 pairs := map[string]domain.PreviewRoute{}
131 for _, preview := range save {
132 pairs[preview.Domain] = preview
133 }
134
135 app, ok := pairs["app--login.example.com"]
136 if !ok || app.BaseRoute != "app.example.com" || !app.HTTPS {
137 continue
138 }
139
140 alias, ok := pairs["alias--login.example.com"]
141 if ok && alias.BaseRoute == "alias.example.com" && !alias.HTTPS {
142 return true
143 }
144 }
145 return false
146 }, time.Second, 10*time.Millisecond)
147}