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