@@ -4,10 +4,12 @@ import (
44 "errors"
55 "fmt"
66 "io"
7+ "slices"
78 "testing"
89
910 "github.com/docker/cli/cli/config/configfile"
1011 "github.com/docker/cli/internal/test"
12+ "github.com/moby/moby/api/types/image"
1113 "github.com/moby/moby/client"
1214 "gotest.tools/v3/assert"
1315 "gotest.tools/v3/golden"
@@ -116,6 +118,86 @@ func TestNewListCommandAmbiguous(t *testing.T) {
116118 golden .Assert (t , cli .ErrBuffer ().String (), "list-command-ambiguous.golden" )
117119}
118120
121+ func TestImagesFilterDangling (t * testing.T ) {
122+ // Create test images with different states
123+ items := []image.Summary {
124+ {
125+ ID : "sha256:87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7" ,
126+ RepoTags : []string {"myimage:latest" },
127+ RepoDigests : []string {"myimage@sha256:abc123" },
128+ },
129+ {
130+ ID : "sha256:0263829989b6fd954f72baaf2fc64bc2e2f01d692d4de72986ea808f6e99813f" ,
131+ RepoTags : []string {},
132+ RepoDigests : []string {},
133+ },
134+ {
135+ ID : "sha256:a3a5e715f0cc574a73c3f9bebb6bc24f32ffd5b67b387244c2c909da779a1478" ,
136+ RepoTags : []string {},
137+ RepoDigests : []string {"image@sha256:a3a5e715f0cc574a73c3f9bebb6bc24f32ffd5b67b387244c2c909da779a1478" },
138+ },
139+ }
140+
141+ testCases := []struct {
142+ name string
143+ args []string
144+ imageListFunc func (options client.ImageListOptions ) (client.ImageListResult , error )
145+ }{
146+ {
147+ name : "dangling-true" ,
148+ args : []string {"-f" , "dangling=true" },
149+ imageListFunc : func (options client.ImageListOptions ) (client.ImageListResult , error ) {
150+ // Verify the filter is passed to the API
151+ assert .Check (t , options .Filters ["dangling" ]["true" ])
152+ // dangling=true is handled on the server side and returns only dangling images
153+ return client.ImageListResult {Items : []image.Summary {items [1 ], items [2 ]}}, nil
154+ },
155+ },
156+ {
157+ name : "dangling-false" ,
158+ args : []string {"-f" , "dangling=false" },
159+ imageListFunc : func (options client.ImageListOptions ) (client.ImageListResult , error ) {
160+ // Verify the filter is passed to the API
161+ assert .Check (t , options .Filters ["dangling" ]["false" ])
162+ // Return all images including dangling
163+ return client.ImageListResult {Items : slices .Clone (items )}, nil
164+ },
165+ },
166+ {
167+ name : "no-dangling-filter" ,
168+ args : []string {},
169+ imageListFunc : func (options client.ImageListOptions ) (client.ImageListResult , error ) {
170+ // Verify no dangling filter is passed to the API
171+ _ , exists := options .Filters ["dangling" ]
172+ assert .Check (t , ! exists )
173+ // Return all images including dangling
174+ return client.ImageListResult {Items : slices .Clone (items )}, nil
175+ },
176+ },
177+ {
178+ name : "all-flag" ,
179+ args : []string {"--all" },
180+ imageListFunc : func (options client.ImageListOptions ) (client.ImageListResult , error ) {
181+ // Verify the All flag is set
182+ assert .Check (t , options .All )
183+ // Return all images including dangling
184+ return client.ImageListResult {Items : slices .Clone (items )}, nil
185+ },
186+ },
187+ }
188+
189+ for _ , tc := range testCases {
190+ t .Run (tc .name , func (t * testing.T ) {
191+ cli := test .NewFakeCli (& fakeClient {imageListFunc : tc .imageListFunc })
192+ cmd := newImagesCommand (cli )
193+ cmd .SetArgs (tc .args )
194+ err := cmd .Execute ()
195+ assert .NilError (t , err )
196+ golden .Assert (t , cli .OutBuffer ().String (), fmt .Sprintf ("list-command-filter-dangling.%s.golden" , tc .name ))
197+ })
198+ }
199+ }
200+
119201func nilToEmptySlice [T any ](s []T ) []T {
120202 if s == nil {
121203 return []T {}
0 commit comments