1package cli
2
3import (
4 "testing"
5
6 "github.com/bnema/gordon/internal/adapters/in/cli/remote"
7 "github.com/bnema/gordon/internal/adapters/in/cli/ui/components"
8 "github.com/stretchr/testify/assert"
9)
10
11func TestTruncateImage(t *testing.T) {
12 tests := []struct {
13 name string
14 image string
15 maxLen int
16 expected string
17 }{
18 // Basic cases - no truncation needed
19 {
20 name: "short image fits",
21 image: "nginx:latest",
22 maxLen: 20,
23 expected: "nginx:latest",
24 },
25 {
26 name: "exact fit",
27 image: "nginx:latest",
28 maxLen: 12,
29 expected: "nginx:latest",
30 },
31
32 // Regular tag truncation
33 {
34 name: "truncate long tag with ellipsis",
35 image: "registry.test.com/test:v1234567890",
36 maxLen: 30,
37 expected: "registry.test.com/test:v123...",
38 },
39 {
40 name: "truncate very long image",
41 image: "registry.example.com/organization/project/image:v1.2.3-beta.4",
42 maxLen: 35,
43 expected: "registry.example.com/organizatio...",
44 },
45
46 // Digest truncation
47 {
48 name: "digest shortened to 12 chars",
49 image: "myapp@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
50 maxLen: 50,
51 expected: "myapp@sha256:a3ed95caeb02",
52 },
53 {
54 name: "digest truncated with ellipsis when too long",
55 image: "registry.example.com/org/app@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
56 maxLen: 35,
57 expected: "registry.example.com/org/app@sha...",
58 },
59 {
60 name: "short digest fits",
61 image: "app@sha256:abc123",
62 maxLen: 30,
63 expected: "app@sha256:abc123",
64 },
65
66 // Edge cases
67 {
68 name: "maxLen zero returns empty",
69 image: "nginx:latest",
70 maxLen: 0,
71 expected: "",
72 },
73 {
74 name: "maxLen negative returns empty",
75 image: "nginx:latest",
76 maxLen: -1,
77 expected: "",
78 },
79 {
80 name: "maxLen 3 or less - no ellipsis",
81 image: "nginx:latest",
82 maxLen: 3,
83 expected: "ngi",
84 },
85 {
86 name: "maxLen 4 - truncate with ellipsis",
87 image: "nginx:latest",
88 maxLen: 4,
89 expected: "n...",
90 },
91 {
92 name: "empty image",
93 image: "",
94 maxLen: 10,
95 expected: "",
96 },
97 }
98
99 for _, tt := range tests {
100 t.Run(tt.name, func(t *testing.T) {
101 result := truncateImage(tt.image, tt.maxLen)
102 assert.Equal(t, tt.expected, result)
103 })
104 }
105}
106
107func TestHTTPHealthToStatus(t *testing.T) {
108 tests := []struct {
109 name string
110 health *remote.RouteHealth
111 expected components.Status
112 }{
113 {
114 name: "nil health returns unknown",
115 health: nil,
116 expected: components.StatusUnknown,
117 },
118 {
119 name: "zero status no error returns unknown",
120 health: &remote.RouteHealth{HTTPStatus: 0},
121 expected: components.StatusUnknown,
122 },
123 {
124 name: "zero status with error returns error",
125 health: &remote.RouteHealth{HTTPStatus: 0, Error: "connection refused"},
126 expected: components.StatusError,
127 },
128 {
129 name: "200 returns success",
130 health: &remote.RouteHealth{HTTPStatus: 200},
131 expected: components.StatusSuccess,
132 },
133 {
134 name: "301 returns success",
135 health: &remote.RouteHealth{HTTPStatus: 301},
136 expected: components.StatusSuccess,
137 },
138 {
139 name: "500 returns error",
140 health: &remote.RouteHealth{HTTPStatus: 500},
141 expected: components.StatusError,
142 },
143 {
144 name: "404 returns error",
145 health: &remote.RouteHealth{HTTPStatus: 404},
146 expected: components.StatusError,
147 },
148 {
149 name: "100 informational returns error",
150 health: &remote.RouteHealth{HTTPStatus: 100},
151 expected: components.StatusError,
152 },
153 }
154
155 for _, tt := range tests {
156 t.Run(tt.name, func(t *testing.T) {
157 result := httpHealthToStatus(tt.health)
158 assert.Equal(t, tt.expected, result)
159 })
160 }
161}
162
163func TestGroupRoutesByNetwork(t *testing.T) {
164 routes := []remote.RouteInfo{
165 {Domain: "alpha.dev", Network: "gordon-alpha-dev"},
166 {Domain: "beta.dev", Network: "gordon-shared"},
167 {Domain: "gamma.dev", Network: "gordon-shared"},
168 {Domain: "delta.dev", Network: "gordon-delta-dev"},
169 }
170
171 groups, solo := groupRoutesByNetwork(routes)
172
173 assert.Len(t, solo, 2)
174 assert.Equal(t, "alpha.dev", solo[0].Domain)
175 assert.Equal(t, "delta.dev", solo[1].Domain)
176
177 assert.Len(t, groups, 1)
178 assert.Equal(t, "shared", groups[0].name)
179 assert.Len(t, groups[0].routes, 2)
180}
181
182func TestStripNetworkPrefix(t *testing.T) {
183 tests := []struct {
184 network string
185 expected string
186 }{
187 {"gordon-shared-services", "shared-services"},
188 {"gordon-my-app-dev", "my-app-dev"},
189 {"custom-network", "custom-network"},
190 {"gordon-", ""},
191 {"", ""},
192 }
193
194 for _, tt := range tests {
195 t.Run(tt.network, func(t *testing.T) {
196 assert.Equal(t, tt.expected, stripNetworkPrefix(tt.network))
197 })
198 }
199}