github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/pkg/jsonmessage/jsonmessage_test.go (about)

     1  package jsonmessage
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/docker/docker/pkg/term"
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  func TestError(t *testing.T) {
    16  	je := JSONError{404, "Not found"}
    17  	if je.Error() != "Not found" {
    18  		t.Fatalf("Expected 'Not found' got '%s'", je.Error())
    19  	}
    20  }
    21  
    22  func TestProgress(t *testing.T) {
    23  	termsz, err := term.GetWinsize(0)
    24  	if err != nil {
    25  		// we can safely ignore the err here
    26  		termsz = nil
    27  	}
    28  	jp := JSONProgress{}
    29  	if jp.String() != "" {
    30  		t.Fatalf("Expected empty string, got '%s'", jp.String())
    31  	}
    32  
    33  	expected := "      1B"
    34  	jp2 := JSONProgress{Current: 1}
    35  	if jp2.String() != expected {
    36  		t.Fatalf("Expected %q, got %q", expected, jp2.String())
    37  	}
    38  
    39  	expectedStart := "[==========>                                        ]      20B/100B"
    40  	if termsz != nil && termsz.Width <= 110 {
    41  		expectedStart = "    20B/100B"
    42  	}
    43  	jp3 := JSONProgress{Current: 20, Total: 100, Start: time.Now().Unix()}
    44  	// Just look at the start of the string
    45  	// (the remaining time is really hard to test -_-)
    46  	if jp3.String()[:len(expectedStart)] != expectedStart {
    47  		t.Fatalf("Expected to start with %q, got %q", expectedStart, jp3.String())
    48  	}
    49  
    50  	expected = "[=========================>                         ]      50B/100B"
    51  	if termsz != nil && termsz.Width <= 110 {
    52  		expected = "    50B/100B"
    53  	}
    54  	jp4 := JSONProgress{Current: 50, Total: 100}
    55  	if jp4.String() != expected {
    56  		t.Fatalf("Expected %q, got %q", expected, jp4.String())
    57  	}
    58  
    59  	// this number can't be negative gh#7136
    60  	expected = "[==================================================>]      50B"
    61  	if termsz != nil && termsz.Width <= 110 {
    62  		expected = "    50B"
    63  	}
    64  	jp5 := JSONProgress{Current: 50, Total: 40}
    65  	if jp5.String() != expected {
    66  		t.Fatalf("Expected %q, got %q", expected, jp5.String())
    67  	}
    68  
    69  	expected = "[=========================>                         ] 50/100 units"
    70  	if termsz != nil && termsz.Width <= 110 {
    71  		expected = "    50/100 units"
    72  	}
    73  	jp6 := JSONProgress{Current: 50, Total: 100, Units: "units"}
    74  	if jp6.String() != expected {
    75  		t.Fatalf("Expected %q, got %q", expected, jp6.String())
    76  	}
    77  
    78  	// this number can't be negative
    79  	expected = "[==================================================>] 50 units"
    80  	if termsz != nil && termsz.Width <= 110 {
    81  		expected = "    50 units"
    82  	}
    83  	jp7 := JSONProgress{Current: 50, Total: 40, Units: "units"}
    84  	if jp7.String() != expected {
    85  		t.Fatalf("Expected %q, got %q", expected, jp7.String())
    86  	}
    87  
    88  	expected = "[=========================>                         ] "
    89  	if termsz != nil && termsz.Width <= 110 {
    90  		expected = ""
    91  	}
    92  	jp8 := JSONProgress{Current: 50, Total: 100, HideCounts: true}
    93  	if jp8.String() != expected {
    94  		t.Fatalf("Expected %q, got %q", expected, jp8.String())
    95  	}
    96  }
    97  
    98  func TestJSONMessageDisplay(t *testing.T) {
    99  	now := time.Now()
   100  	messages := map[JSONMessage][]string{
   101  		// Empty
   102  		{}: {"\n", "\n"},
   103  		// Status
   104  		{
   105  			Status: "status",
   106  		}: {
   107  			"status\n",
   108  			"status\n",
   109  		},
   110  		// General
   111  		{
   112  			Time:   now.Unix(),
   113  			ID:     "ID",
   114  			From:   "From",
   115  			Status: "status",
   116  		}: {
   117  			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(RFC3339NanoFixed)),
   118  			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(RFC3339NanoFixed)),
   119  		},
   120  		// General, with nano precision time
   121  		{
   122  			TimeNano: now.UnixNano(),
   123  			ID:       "ID",
   124  			From:     "From",
   125  			Status:   "status",
   126  		}: {
   127  			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(RFC3339NanoFixed)),
   128  			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(RFC3339NanoFixed)),
   129  		},
   130  		// General, with both times Nano is preferred
   131  		{
   132  			Time:     now.Unix(),
   133  			TimeNano: now.UnixNano(),
   134  			ID:       "ID",
   135  			From:     "From",
   136  			Status:   "status",
   137  		}: {
   138  			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(RFC3339NanoFixed)),
   139  			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(RFC3339NanoFixed)),
   140  		},
   141  		// Stream over status
   142  		{
   143  			Status: "status",
   144  			Stream: "stream",
   145  		}: {
   146  			"stream",
   147  			"stream",
   148  		},
   149  		// With progress message
   150  		{
   151  			Status:          "status",
   152  			ProgressMessage: "progressMessage",
   153  		}: {
   154  			"status progressMessage",
   155  			"status progressMessage",
   156  		},
   157  		// With progress, stream empty
   158  		{
   159  			Status:   "status",
   160  			Stream:   "",
   161  			Progress: &JSONProgress{Current: 1},
   162  		}: {
   163  			"",
   164  			fmt.Sprintf("%c[1K%c[K\rstatus       1B\r", 27, 27),
   165  		},
   166  	}
   167  
   168  	// The tests :)
   169  	for jsonMessage, expectedMessages := range messages {
   170  		// Without terminal
   171  		data := bytes.NewBuffer([]byte{})
   172  		if err := jsonMessage.Display(data, nil); err != nil {
   173  			t.Fatal(err)
   174  		}
   175  		if data.String() != expectedMessages[0] {
   176  			t.Fatalf("Expected %q,got %q", expectedMessages[0], data.String())
   177  		}
   178  		// With terminal
   179  		data = bytes.NewBuffer([]byte{})
   180  		if err := jsonMessage.Display(data, &noTermInfo{}); err != nil {
   181  			t.Fatal(err)
   182  		}
   183  		if data.String() != expectedMessages[1] {
   184  			t.Fatalf("\nExpected %q\n     got %q", expectedMessages[1], data.String())
   185  		}
   186  	}
   187  }
   188  
   189  // Test JSONMessage with an Error. It will return an error with the text as error, not the meaning of the HTTP code.
   190  func TestJSONMessageDisplayWithJSONError(t *testing.T) {
   191  	data := bytes.NewBuffer([]byte{})
   192  	jsonMessage := JSONMessage{Error: &JSONError{404, "Can't find it"}}
   193  
   194  	err := jsonMessage.Display(data, &noTermInfo{})
   195  	if err == nil || err.Error() != "Can't find it" {
   196  		t.Fatalf("Expected a JSONError 404, got %q", err)
   197  	}
   198  
   199  	jsonMessage = JSONMessage{Error: &JSONError{401, "Anything"}}
   200  	err = jsonMessage.Display(data, &noTermInfo{})
   201  	assert.EqualError(t, err, "authentication is required")
   202  }
   203  
   204  func TestDisplayJSONMessagesStreamInvalidJSON(t *testing.T) {
   205  	var (
   206  		inFd uintptr
   207  	)
   208  	data := bytes.NewBuffer([]byte{})
   209  	reader := strings.NewReader("This is not a 'valid' JSON []")
   210  	inFd, _ = term.GetFdInfo(reader)
   211  
   212  	if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err == nil && err.Error()[:17] != "invalid character" {
   213  		t.Fatalf("Should have thrown an error (invalid character in ..), got %q", err)
   214  	}
   215  }
   216  
   217  func TestDisplayJSONMessagesStream(t *testing.T) {
   218  	var (
   219  		inFd uintptr
   220  	)
   221  
   222  	messages := map[string][]string{
   223  		// empty string
   224  		"": {
   225  			"",
   226  			""},
   227  		// Without progress & ID
   228  		"{ \"status\": \"status\" }": {
   229  			"status\n",
   230  			"status\n",
   231  		},
   232  		// Without progress, with ID
   233  		"{ \"id\": \"ID\",\"status\": \"status\" }": {
   234  			"ID: status\n",
   235  			fmt.Sprintf("ID: status\n"),
   236  		},
   237  		// With progress
   238  		"{ \"id\": \"ID\", \"status\": \"status\", \"progress\": \"ProgressMessage\" }": {
   239  			"ID: status ProgressMessage",
   240  			fmt.Sprintf("\n%c[%dAID: status ProgressMessage%c[%dB", 27, 1, 27, 1),
   241  		},
   242  		// With progressDetail
   243  		"{ \"id\": \"ID\", \"status\": \"status\", \"progressDetail\": { \"Current\": 1} }": {
   244  			"", // progressbar is disabled in non-terminal
   245  			fmt.Sprintf("\n%c[%dA%c[1K%c[K\rID: status       1B\r%c[%dB", 27, 1, 27, 27, 27, 1),
   246  		},
   247  	}
   248  
   249  	// Use $TERM which is unlikely to exist, forcing DisplayJSONMessageStream to
   250  	// (hopefully) use &noTermInfo.
   251  	origTerm := os.Getenv("TERM")
   252  	os.Setenv("TERM", "xyzzy-non-existent-terminfo")
   253  
   254  	for jsonMessage, expectedMessages := range messages {
   255  		data := bytes.NewBuffer([]byte{})
   256  		reader := strings.NewReader(jsonMessage)
   257  		inFd, _ = term.GetFdInfo(reader)
   258  
   259  		// Without terminal
   260  		if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err != nil {
   261  			t.Fatal(err)
   262  		}
   263  		if data.String() != expectedMessages[0] {
   264  			t.Fatalf("Expected an %q, got %q", expectedMessages[0], data.String())
   265  		}
   266  
   267  		// With terminal
   268  		data = bytes.NewBuffer([]byte{})
   269  		reader = strings.NewReader(jsonMessage)
   270  		if err := DisplayJSONMessagesStream(reader, data, inFd, true, nil); err != nil {
   271  			t.Fatal(err)
   272  		}
   273  		if data.String() != expectedMessages[1] {
   274  			t.Fatalf("\nExpected %q\n     got %q", expectedMessages[1], data.String())
   275  		}
   276  	}
   277  	os.Setenv("TERM", origTerm)
   278  
   279  }