diff --git a/command/idresolver/client_test.go b/command/idresolver/client_test.go new file mode 100644 index 0000000000..8c02d7ebcf --- /dev/null +++ b/command/idresolver/client_test.go @@ -0,0 +1,27 @@ +package idresolver + +import ( + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/client" + "golang.org/x/net/context" +) + +type fakeClient struct { + client.Client + nodeInspectFunc func(string) (swarm.Node, []byte, error) + serviceInspectFunc func(string) (swarm.Service, []byte, error) +} + +func (cli *fakeClient) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) { + if cli.nodeInspectFunc != nil { + return cli.nodeInspectFunc(nodeID) + } + return swarm.Node{}, []byte{}, nil +} + +func (cli *fakeClient) ServiceInspectWithRaw(ctx context.Context, serviceID string) (swarm.Service, []byte, error) { + if cli.serviceInspectFunc != nil { + return cli.serviceInspectFunc(serviceID) + } + return swarm.Service{}, []byte{}, nil +} diff --git a/command/idresolver/idresolver_test.go b/command/idresolver/idresolver_test.go new file mode 100644 index 0000000000..720667daa1 --- /dev/null +++ b/command/idresolver/idresolver_test.go @@ -0,0 +1,144 @@ +package idresolver + +import ( + "testing" + + "github.com/docker/docker/api/types/swarm" + // Import builders to get the builder function as package function + . "github.com/docker/docker/cli/internal/test/builders" + "github.com/docker/docker/pkg/testutil/assert" + "github.com/pkg/errors" + "golang.org/x/net/context" +) + +func TestResolveError(t *testing.T) { + cli := &fakeClient{ + nodeInspectFunc: func(nodeID string) (swarm.Node, []byte, error) { + return swarm.Node{}, []byte{}, errors.Errorf("error inspecting node") + }, + } + + idResolver := New(cli, false) + _, err := idResolver.Resolve(context.Background(), struct{}{}, "nodeID") + + assert.Error(t, err, "unsupported type") +} + +func TestResolveWithNoResolveOption(t *testing.T) { + resolved := false + cli := &fakeClient{ + nodeInspectFunc: func(nodeID string) (swarm.Node, []byte, error) { + resolved = true + return swarm.Node{}, []byte{}, nil + }, + serviceInspectFunc: func(serviceID string) (swarm.Service, []byte, error) { + resolved = true + return swarm.Service{}, []byte{}, nil + }, + } + + idResolver := New(cli, true) + id, err := idResolver.Resolve(context.Background(), swarm.Node{}, "nodeID") + + assert.NilError(t, err) + assert.Equal(t, id, "nodeID") + assert.Equal(t, resolved, false) +} + +func TestResolveWithCache(t *testing.T) { + inspectCounter := 0 + cli := &fakeClient{ + nodeInspectFunc: func(nodeID string) (swarm.Node, []byte, error) { + inspectCounter++ + return *Node(NodeName("node-foo")), []byte{}, nil + }, + } + + idResolver := New(cli, false) + + ctx := context.Background() + for i := 0; i < 2; i++ { + id, err := idResolver.Resolve(ctx, swarm.Node{}, "nodeID") + assert.NilError(t, err) + assert.Equal(t, id, "node-foo") + } + + assert.Equal(t, inspectCounter, 1) +} + +func TestResolveNode(t *testing.T) { + testCases := []struct { + nodeID string + nodeInspectFunc func(string) (swarm.Node, []byte, error) + expectedID string + }{ + { + nodeID: "nodeID", + nodeInspectFunc: func(string) (swarm.Node, []byte, error) { + return swarm.Node{}, []byte{}, errors.Errorf("error inspecting node") + }, + expectedID: "nodeID", + }, + { + nodeID: "nodeID", + nodeInspectFunc: func(string) (swarm.Node, []byte, error) { + return *Node(NodeName("node-foo")), []byte{}, nil + }, + expectedID: "node-foo", + }, + { + nodeID: "nodeID", + nodeInspectFunc: func(string) (swarm.Node, []byte, error) { + return *Node(NodeName(""), Hostname("node-hostname")), []byte{}, nil + }, + expectedID: "node-hostname", + }, + } + + ctx := context.Background() + for _, tc := range testCases { + cli := &fakeClient{ + nodeInspectFunc: tc.nodeInspectFunc, + } + idResolver := New(cli, false) + id, err := idResolver.Resolve(ctx, swarm.Node{}, tc.nodeID) + + assert.NilError(t, err) + assert.Equal(t, id, tc.expectedID) + } +} + +func TestResolveService(t *testing.T) { + testCases := []struct { + serviceID string + serviceInspectFunc func(string) (swarm.Service, []byte, error) + expectedID string + }{ + { + serviceID: "serviceID", + serviceInspectFunc: func(string) (swarm.Service, []byte, error) { + return swarm.Service{}, []byte{}, errors.Errorf("error inspecting service") + }, + expectedID: "serviceID", + }, + { + serviceID: "serviceID", + serviceInspectFunc: func(string) (swarm.Service, []byte, error) { + return *Service(ServiceName("service-foo")), []byte{}, nil + }, + expectedID: "service-foo", + }, + } + + ctx := context.Background() + for _, tc := range testCases { + cli := &fakeClient{ + serviceInspectFunc: tc.serviceInspectFunc, + } + idResolver := New(cli, false) + id, err := idResolver.Resolve(ctx, swarm.Service{}, tc.serviceID) + + assert.NilError(t, err) + assert.Equal(t, id, tc.expectedID) + } +} diff --git a/internal/test/builders/node.go b/internal/test/builders/node.go index 040955785c..234d6de242 100644 --- a/internal/test/builders/node.go +++ b/internal/test/builders/node.go @@ -76,6 +76,13 @@ func NodeID(id string) func(*swarm.Node) { } } +// NodeName sets the node name +func NodeName(name string) func(*swarm.Node) { + return func(node *swarm.Node) { + node.Spec.Annotations.Name = name + } +} + // NodeLabels sets the node labels func NodeLabels(labels map[string]string) func(*swarm.Node) { return func(node *swarm.Node) { diff --git a/internal/test/builders/service.go b/internal/test/builders/service.go new file mode 100644 index 0000000000..dee91f4baa --- /dev/null +++ b/internal/test/builders/service.go @@ -0,0 +1,32 @@ +package builders + +import ( + "github.com/docker/docker/api/types/swarm" +) + +// Service creates a service with default values. +// Any number of service builder functions can be passed to augment it. +// Currently, only ServiceName is implemented +func Service(builders ...func(*swarm.Service)) *swarm.Service { + service := &swarm.Service{ + ID: "serviceID", + Spec: swarm.ServiceSpec{ + Annotations: swarm.Annotations{ + Name: "defaultServiceName", + }, + }, + } + + for _, builder := range builders { + builder(service) + } + + return service +} + +// ServiceName sets the service name +func ServiceName(name string) func(*swarm.Service) { + return func(service *swarm.Service) { + service.Spec.Annotations.Name = name + } +}