github.com/moby/docker@v26.1.3+incompatible/client/ping_test.go (about)

     1  package client // import "github.com/docker/docker/client"
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"net/http"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/docker/docker/api/types/swarm"
    12  	"gotest.tools/v3/assert"
    13  	is "gotest.tools/v3/assert/cmp"
    14  )
    15  
    16  // TestPingFail tests that when a server sends a non-successful response that we
    17  // can still grab API details, when set.
    18  // Some of this is just exercising the code paths to make sure there are no
    19  // panics.
    20  func TestPingFail(t *testing.T) {
    21  	var withHeader bool
    22  	client := &Client{
    23  		client: newMockClient(func(req *http.Request) (*http.Response, error) {
    24  			resp := &http.Response{StatusCode: http.StatusInternalServerError}
    25  			if withHeader {
    26  				resp.Header = http.Header{}
    27  				resp.Header.Set("API-Version", "awesome")
    28  				resp.Header.Set("Docker-Experimental", "true")
    29  				resp.Header.Set("Swarm", "inactive")
    30  			}
    31  			resp.Body = io.NopCloser(strings.NewReader("some error with the server"))
    32  			return resp, nil
    33  		}),
    34  	}
    35  
    36  	ping, err := client.Ping(context.Background())
    37  	assert.Check(t, is.ErrorContains(err, "some error with the server"))
    38  	assert.Check(t, is.Equal(false, ping.Experimental))
    39  	assert.Check(t, is.Equal("", ping.APIVersion))
    40  	var si *swarm.Status
    41  	assert.Check(t, is.Equal(si, ping.SwarmStatus))
    42  
    43  	withHeader = true
    44  	ping2, err := client.Ping(context.Background())
    45  	assert.Check(t, is.ErrorContains(err, "some error with the server"))
    46  	assert.Check(t, is.Equal(true, ping2.Experimental))
    47  	assert.Check(t, is.Equal("awesome", ping2.APIVersion))
    48  	assert.Check(t, is.Equal(swarm.Status{NodeState: "inactive"}, *ping2.SwarmStatus))
    49  }
    50  
    51  // TestPingWithError tests the case where there is a protocol error in the ping.
    52  // This test is mostly just testing that there are no panics in this code path.
    53  func TestPingWithError(t *testing.T) {
    54  	client := &Client{
    55  		client: newMockClient(func(req *http.Request) (*http.Response, error) {
    56  			return nil, errors.New("some connection error")
    57  		}),
    58  	}
    59  
    60  	ping, err := client.Ping(context.Background())
    61  	assert.Check(t, is.ErrorContains(err, "some connection error"))
    62  	assert.Check(t, is.Equal(false, ping.Experimental))
    63  	assert.Check(t, is.Equal("", ping.APIVersion))
    64  	var si *swarm.Status
    65  	assert.Check(t, is.Equal(si, ping.SwarmStatus))
    66  }
    67  
    68  // TestPingSuccess tests that we are able to get the expected API headers/ping
    69  // details on success.
    70  func TestPingSuccess(t *testing.T) {
    71  	client := &Client{
    72  		client: newMockClient(func(req *http.Request) (*http.Response, error) {
    73  			resp := &http.Response{StatusCode: http.StatusOK}
    74  			resp.Header = http.Header{}
    75  			resp.Header.Set("API-Version", "awesome")
    76  			resp.Header.Set("Docker-Experimental", "true")
    77  			resp.Header.Set("Swarm", "active/manager")
    78  			resp.Body = io.NopCloser(strings.NewReader("OK"))
    79  			return resp, nil
    80  		}),
    81  	}
    82  	ping, err := client.Ping(context.Background())
    83  	assert.NilError(t, err)
    84  	assert.Check(t, is.Equal(true, ping.Experimental))
    85  	assert.Check(t, is.Equal("awesome", ping.APIVersion))
    86  	assert.Check(t, is.Equal(swarm.Status{NodeState: "active", ControlAvailable: true}, *ping.SwarmStatus))
    87  }
    88  
    89  // TestPingHeadFallback tests that the client falls back to GET if HEAD fails.
    90  func TestPingHeadFallback(t *testing.T) {
    91  	tests := []struct {
    92  		status   int
    93  		expected string
    94  	}{
    95  		{
    96  			status:   http.StatusOK,
    97  			expected: http.MethodHead,
    98  		},
    99  		{
   100  			status:   http.StatusInternalServerError,
   101  			expected: http.MethodHead,
   102  		},
   103  		{
   104  			status:   http.StatusNotFound,
   105  			expected: "HEAD, GET",
   106  		},
   107  		{
   108  			status:   http.StatusMethodNotAllowed,
   109  			expected: "HEAD, GET",
   110  		},
   111  	}
   112  
   113  	for _, tc := range tests {
   114  		tc := tc
   115  		t.Run(http.StatusText(tc.status), func(t *testing.T) {
   116  			var reqs []string
   117  			client := &Client{
   118  				client: newMockClient(func(req *http.Request) (*http.Response, error) {
   119  					reqs = append(reqs, req.Method)
   120  					resp := &http.Response{StatusCode: http.StatusOK}
   121  					if req.Method == http.MethodHead {
   122  						resp.StatusCode = tc.status
   123  					}
   124  					resp.Header = http.Header{}
   125  					resp.Header.Add("API-Version", strings.Join(reqs, ", "))
   126  					return resp, nil
   127  				}),
   128  			}
   129  			ping, _ := client.Ping(context.Background())
   130  			assert.Check(t, is.Equal(ping.APIVersion, tc.expected))
   131  		})
   132  	}
   133  }