@@ -289,4 +289,82 @@ func TestAggregateEndpointSlices(t *testing.T) {
289289 require .Len (t , result .Subsets , 1 )
290290 assert .Equal (t , "" , result .Subsets [0 ].Ports [0 ].Name )
291291 })
292+
293+ t .Run ("slice with empty endpoints array" , func (t * testing.T ) {
294+ // Tests a slice that has valid ports but an empty Endpoints array
295+ // (not just all non-ready endpoints). This can happen temporarily
296+ // during rolling deployments.
297+ slices := []* discoveryv1.EndpointSlice {{
298+ Ports : []discoveryv1.EndpointPort {{
299+ Name : ptr .To ("http" ),
300+ Port : ptr .To (int32 (8080 )),
301+ Protocol : ptr .To (corev1 .ProtocolTCP ),
302+ }},
303+ Endpoints : []discoveryv1.Endpoint {}, // Empty, not just non-ready
304+ }}
305+
306+ result := AggregateEndpointSlices (slices )
307+
308+ assert .Empty (t , result .Subsets )
309+ })
310+
311+ t .Run ("same port different protocols creates separate subsets" , func (t * testing.T ) {
312+ // Tests that ports with the same number but different protocols
313+ // are kept separate (e.g., DNS service using both TCP and UDP on port 53)
314+ slices := []* discoveryv1.EndpointSlice {{
315+ Ports : []discoveryv1.EndpointPort {
316+ {Name : ptr .To ("dns-tcp" ), Port : ptr .To (int32 (53 )), Protocol : ptr .To (corev1 .ProtocolTCP )},
317+ {Name : ptr .To ("dns-udp" ), Port : ptr .To (int32 (53 )), Protocol : ptr .To (corev1 .ProtocolUDP )},
318+ },
319+ Endpoints : []discoveryv1.Endpoint {{
320+ Addresses : []string {"10.0.0.1" },
321+ Conditions : discoveryv1.EndpointConditions {Ready : ptr .To (true )},
322+ }},
323+ }}
324+
325+ result := AggregateEndpointSlices (slices )
326+
327+ require .Len (t , result .Subsets , 2 )
328+ // Subsets are sorted by port name, so dns-tcp comes before dns-udp
329+ assert .Equal (t , "dns-tcp" , result .Subsets [0 ].Ports [0 ].Name )
330+ assert .Equal (t , corev1 .ProtocolTCP , result .Subsets [0 ].Ports [0 ].Protocol )
331+ assert .Equal (t , "dns-udp" , result .Subsets [1 ].Ports [0 ].Name )
332+ assert .Equal (t , corev1 .ProtocolUDP , result .Subsets [1 ].Ports [0 ].Protocol )
333+ })
334+
335+ t .Run ("multiple slices with different ports" , func (t * testing.T ) {
336+ // Tests that multiple slices with different ports are aggregated correctly
337+ // This can happen with large services where endpoints are split across slices
338+ slices := []* discoveryv1.EndpointSlice {
339+ {
340+ Ports : []discoveryv1.EndpointPort {{
341+ Name : ptr .To ("http" ), Port : ptr .To (int32 (8080 )), Protocol : ptr .To (corev1 .ProtocolTCP ),
342+ }},
343+ Endpoints : []discoveryv1.Endpoint {{
344+ Addresses : []string {"10.0.0.1" },
345+ Conditions : discoveryv1.EndpointConditions {Ready : ptr .To (true )},
346+ }},
347+ },
348+ {
349+ Ports : []discoveryv1.EndpointPort {{
350+ Name : ptr .To ("grpc" ), Port : ptr .To (int32 (9090 )), Protocol : ptr .To (corev1 .ProtocolTCP ),
351+ }},
352+ Endpoints : []discoveryv1.Endpoint {{
353+ Addresses : []string {"10.0.0.2" },
354+ Conditions : discoveryv1.EndpointConditions {Ready : ptr .To (true )},
355+ }},
356+ },
357+ }
358+
359+ result := AggregateEndpointSlices (slices )
360+
361+ require .Len (t , result .Subsets , 2 )
362+ // Subsets are sorted by port name, so grpc comes before http
363+ assert .Equal (t , "grpc" , result .Subsets [0 ].Ports [0 ].Name )
364+ assert .Equal (t , int32 (9090 ), result .Subsets [0 ].Ports [0 ].Port )
365+ assert .Equal (t , "10.0.0.2" , result .Subsets [0 ].Addresses [0 ].IP )
366+ assert .Equal (t , "http" , result .Subsets [1 ].Ports [0 ].Name )
367+ assert .Equal (t , int32 (8080 ), result .Subsets [1 ].Ports [0 ].Port )
368+ assert .Equal (t , "10.0.0.1" , result .Subsets [1 ].Addresses [0 ].IP )
369+ })
292370}
0 commit comments