jsonmessage_test.go 7.9 KB

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