123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- package jsonmessage // import "github.com/docker/docker/pkg/jsonmessage"
- import (
- "bytes"
- "fmt"
- "strings"
- "testing"
- "time"
- "github.com/moby/term"
- "gotest.tools/v3/assert"
- is "gotest.tools/v3/assert/cmp"
- )
- func TestError(t *testing.T) {
- je := JSONError{404, "Not found"}
- assert.Assert(t, is.Error(&je, "Not found"))
- }
- func TestProgressString(t *testing.T) {
- type expected struct {
- short string
- long string
- }
- shortAndLong := func(short, long string) expected {
- return expected{short: short, long: long}
- }
- start := time.Date(2017, 12, 3, 15, 10, 1, 0, time.UTC)
- timeAfter := func(delta time.Duration) func() time.Time {
- return func() time.Time {
- return start.Add(delta)
- }
- }
- var testcases = []struct {
- name string
- progress JSONProgress
- expected expected
- }{
- {
- name: "no progress",
- },
- {
- name: "progress 1",
- progress: JSONProgress{Current: 1},
- expected: shortAndLong(" 1B", " 1B"),
- },
- {
- name: "some progress with a start time",
- progress: JSONProgress{
- Current: 20,
- Total: 100,
- Start: start.Unix(),
- nowFunc: timeAfter(time.Second),
- },
- expected: shortAndLong(
- " 20B/100B 4s",
- "[==========> ] 20B/100B 4s",
- ),
- },
- {
- name: "some progress without a start time",
- progress: JSONProgress{Current: 50, Total: 100},
- expected: shortAndLong(
- " 50B/100B",
- "[=========================> ] 50B/100B",
- ),
- },
- {
- name: "current more than total is not negative gh#7136",
- progress: JSONProgress{Current: 50, Total: 40},
- expected: shortAndLong(
- " 50B",
- "[==================================================>] 50B",
- ),
- },
- {
- name: "with units",
- progress: JSONProgress{Current: 50, Total: 100, Units: "units"},
- expected: shortAndLong(
- "50/100 units",
- "[=========================> ] 50/100 units",
- ),
- },
- {
- name: "current more than total with units is not negative ",
- progress: JSONProgress{Current: 50, Total: 40, Units: "units"},
- expected: shortAndLong(
- "50 units",
- "[==================================================>] 50 units",
- ),
- },
- {
- name: "hide counts",
- progress: JSONProgress{Current: 50, Total: 100, HideCounts: true},
- expected: shortAndLong(
- "",
- "[=========================> ] ",
- ),
- },
- }
- for _, testcase := range testcases {
- t.Run(testcase.name, func(t *testing.T) {
- testcase.progress.winSize = 100
- assert.Equal(t, testcase.progress.String(), testcase.expected.short)
- testcase.progress.winSize = 200
- assert.Equal(t, testcase.progress.String(), testcase.expected.long)
- })
- }
- }
- func TestJSONMessageDisplay(t *testing.T) {
- now := time.Now()
- messages := map[JSONMessage][]string{
- // Empty
- {}: {"\n", "\n"},
- // Status
- {
- Status: "status",
- }: {
- "status\n",
- "status\n",
- },
- // General
- {
- Time: now.Unix(),
- ID: "ID",
- From: "From",
- Status: "status",
- }: {
- fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(RFC3339NanoFixed)),
- fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(RFC3339NanoFixed)),
- },
- // General, with nano precision time
- {
- TimeNano: now.UnixNano(),
- ID: "ID",
- From: "From",
- Status: "status",
- }: {
- fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(RFC3339NanoFixed)),
- fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(RFC3339NanoFixed)),
- },
- // General, with both times Nano is preferred
- {
- Time: now.Unix(),
- TimeNano: now.UnixNano(),
- ID: "ID",
- From: "From",
- Status: "status",
- }: {
- fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(RFC3339NanoFixed)),
- fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(RFC3339NanoFixed)),
- },
- // Stream over status
- {
- Status: "status",
- Stream: "stream",
- }: {
- "stream",
- "stream",
- },
- // With progress message
- {
- Status: "status",
- ProgressMessage: "progressMessage",
- }: {
- "status progressMessage",
- "status progressMessage",
- },
- // With progress, stream empty
- {
- Status: "status",
- Stream: "",
- Progress: &JSONProgress{Current: 1},
- }: {
- "",
- fmt.Sprintf("%c[2K\rstatus 1B\r", 27),
- },
- }
- // The tests :)
- for jsonMessage, expectedMessages := range messages {
- // Without terminal
- data := bytes.NewBuffer([]byte{})
- if err := jsonMessage.Display(data, false); err != nil {
- t.Fatal(err)
- }
- if data.String() != expectedMessages[0] {
- t.Fatalf("Expected %q,got %q", expectedMessages[0], data.String())
- }
- // With terminal
- data = bytes.NewBuffer([]byte{})
- if err := jsonMessage.Display(data, true); err != nil {
- t.Fatal(err)
- }
- if data.String() != expectedMessages[1] {
- t.Fatalf("\nExpected %q\n got %q", expectedMessages[1], data.String())
- }
- }
- }
- // Test JSONMessage with an Error. It will return an error with the text as error, not the meaning of the HTTP code.
- func TestJSONMessageDisplayWithJSONError(t *testing.T) {
- data := bytes.NewBuffer([]byte{})
- jsonMessage := JSONMessage{Error: &JSONError{404, "Can't find it"}}
- err := jsonMessage.Display(data, true)
- if err == nil || err.Error() != "Can't find it" {
- t.Fatalf("Expected a JSONError 404, got %q", err)
- }
- jsonMessage = JSONMessage{Error: &JSONError{401, "Anything"}}
- err = jsonMessage.Display(data, true)
- assert.Check(t, is.Error(err, "authentication is required"))
- }
- func TestDisplayJSONMessagesStreamInvalidJSON(t *testing.T) {
- var (
- inFd uintptr
- )
- data := bytes.NewBuffer([]byte{})
- reader := strings.NewReader("This is not a 'valid' JSON []")
- inFd, _ = term.GetFdInfo(reader)
- exp := "invalid character "
- if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err == nil || !strings.HasPrefix(err.Error(), exp) {
- t.Fatalf("Expected error (%s...), got %q", exp, err)
- }
- }
- func TestDisplayJSONMessagesStream(t *testing.T) {
- var (
- inFd uintptr
- )
- messages := map[string][]string{
- // empty string
- "": {
- "",
- ""},
- // Without progress & ID
- "{ \"status\": \"status\" }": {
- "status\n",
- "status\n",
- },
- // Without progress, with ID
- "{ \"id\": \"ID\",\"status\": \"status\" }": {
- "ID: status\n",
- "ID: status\n",
- },
- // With progress
- "{ \"id\": \"ID\", \"status\": \"status\", \"progress\": \"ProgressMessage\" }": {
- "ID: status ProgressMessage",
- fmt.Sprintf("\n%c[%dAID: status ProgressMessage%c[%dB", 27, 1, 27, 1),
- },
- // With progressDetail
- "{ \"id\": \"ID\", \"status\": \"status\", \"progressDetail\": { \"Current\": 1} }": {
- "", // progressbar is disabled in non-terminal
- fmt.Sprintf("\n%c[%dA%c[2K\rID: status 1B\r%c[%dB", 27, 1, 27, 27, 1),
- },
- }
- // Use $TERM which is unlikely to exist, forcing DisplayJSONMessageStream to
- // (hopefully) use &noTermInfo.
- t.Setenv("TERM", "xyzzy-non-existent-terminfo")
- for jsonMessage, expectedMessages := range messages {
- data := bytes.NewBuffer([]byte{})
- reader := strings.NewReader(jsonMessage)
- inFd, _ = term.GetFdInfo(reader)
- // Without terminal
- if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err != nil {
- t.Fatal(err)
- }
- if data.String() != expectedMessages[0] {
- t.Fatalf("Expected an %q, got %q", expectedMessages[0], data.String())
- }
- // With terminal
- data = bytes.NewBuffer([]byte{})
- reader = strings.NewReader(jsonMessage)
- if err := DisplayJSONMessagesStream(reader, data, inFd, true, nil); err != nil {
- t.Fatal(err)
- }
- if data.String() != expectedMessages[1] {
- t.Fatalf("\nExpected %q\n got %q", expectedMessages[1], data.String())
- }
- }
- }
|