1package preview
2
3import (
4 "testing"
5 "time"
6
7 "github.com/stretchr/testify/assert"
8
9 "github.com/bnema/gordon/internal/domain"
10)
11
12func TestIsOrphanPreview(t *testing.T) {
13 patterns := []string{"preview-*"}
14 separator := "--"
15
16 tests := []struct {
17 name string
18 c *domain.Container
19 tracked []domain.PreviewRoute
20 want bool
21 }{
22 {
23 name: "orphan preview container",
24 c: &domain.Container{
25 Created: time.Now().Add(-1 * time.Hour),
26 Labels: map[string]string{
27 domain.LabelImage: "myapp:preview-feat",
28 domain.LabelDomain: "myapp--feat.example.com",
29 },
30 },
31 tracked: nil,
32 want: true,
33 },
34 {
35 name: "tracked preview is not orphan",
36 c: &domain.Container{
37 Created: time.Now().Add(-1 * time.Hour),
38 Labels: map[string]string{
39 domain.LabelImage: "myapp:preview-feat",
40 domain.LabelDomain: "myapp--feat.example.com",
41 },
42 },
43 tracked: []domain.PreviewRoute{
44 {Domain: "myapp--feat.example.com"},
45 },
46 want: false,
47 },
48 {
49 name: "production container — no separator in domain",
50 c: &domain.Container{
51 Created: time.Now().Add(-1 * time.Hour),
52 Labels: map[string]string{
53 domain.LabelImage: "myapp:preview-release",
54 domain.LabelDomain: "myapp.example.com",
55 },
56 },
57 tracked: nil,
58 want: false,
59 },
60 {
61 name: "production container — no preview tag",
62 c: &domain.Container{
63 Created: time.Now().Add(-1 * time.Hour),
64 Labels: map[string]string{
65 domain.LabelImage: "myapp:latest",
66 domain.LabelDomain: "myapp--feat.example.com",
67 },
68 },
69 tracked: nil,
70 want: false,
71 },
72 {
73 name: "container without gordon labels",
74 c: &domain.Container{
75 Labels: map[string]string{},
76 },
77 tracked: nil,
78 want: false,
79 },
80 {
81 name: "container too young — grace period",
82 c: &domain.Container{
83 Created: time.Now().Add(-2 * time.Minute),
84 Labels: map[string]string{
85 domain.LabelImage: "myapp:preview-feat",
86 domain.LabelDomain: "myapp--feat.example.com",
87 },
88 },
89 tracked: nil,
90 want: false,
91 },
92 {
93 name: "digest-only image — no tag to match",
94 c: &domain.Container{
95 Created: time.Now().Add(-1 * time.Hour),
96 Labels: map[string]string{
97 domain.LabelImage: "myapp@sha256:abc123",
98 domain.LabelDomain: "myapp--feat.example.com",
99 },
100 },
101 tracked: nil,
102 want: false,
103 },
104 }
105
106 for _, tt := range tests {
107 t.Run(tt.name, func(t *testing.T) {
108 got := IsOrphanPreview(tt.c, tt.tracked, patterns, separator)
109 assert.Equal(t, tt.want, got)
110 })
111 }
112}
113
114func TestIsExpiredOrphan(t *testing.T) {
115 ttl := 48 * time.Hour
116
117 tests := []struct {
118 name string
119 created time.Time
120 want bool
121 }{
122 {
123 name: "expired — older than TTL",
124 created: time.Now().Add(-72 * time.Hour),
125 want: true,
126 },
127 {
128 name: "not expired — within TTL",
129 created: time.Now().Add(-12 * time.Hour),
130 want: false,
131 },
132 {
133 name: "zero time — unknown, skip to be safe",
134 created: time.Time{},
135 want: false,
136 },
137 }
138
139 for _, tt := range tests {
140 t.Run(tt.name, func(t *testing.T) {
141 c := &domain.Container{Created: tt.created}
142 got := IsExpiredOrphan(c, ttl)
143 assert.Equal(t, tt.want, got)
144 })
145 }
146}
147
148func TestTagFromImage(t *testing.T) {
149 tests := []struct {
150 image string
151 want string
152 }{
153 {"myapp:preview-feat", "preview-feat"},
154 {"registry.example.com/myapp:preview-feat", "preview-feat"},
155 {"myapp:latest", "latest"},
156 {"myapp", ""},
157 {"myapp@sha256:abc123", ""},
158 }
159
160 for _, tt := range tests {
161 t.Run(tt.image, func(t *testing.T) {
162 assert.Equal(t, tt.want, tagFromImage(tt.image))
163 })
164 }
165}