You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

282 lines
5.2 KiB

6 years ago
  1. package httpheader
  2. import (
  3. "fmt"
  4. "net/http"
  5. "reflect"
  6. "testing"
  7. "time"
  8. )
  9. func TestHeader_types(t *testing.T) {
  10. str := "string"
  11. strPtr := &str
  12. timeVal := time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)
  13. tests := []struct {
  14. in interface{}
  15. want http.Header
  16. }{
  17. {
  18. // basic primitives
  19. struct {
  20. A string
  21. B int
  22. C uint
  23. D float32
  24. E bool
  25. }{},
  26. http.Header{
  27. "A": []string{""},
  28. "B": []string{"0"},
  29. "C": []string{"0"},
  30. "D": []string{"0"},
  31. "E": []string{"false"},
  32. },
  33. },
  34. {
  35. // pointers
  36. struct {
  37. A *string
  38. B *int
  39. C **string
  40. D *time.Time
  41. }{
  42. A: strPtr,
  43. C: &strPtr,
  44. D: &timeVal,
  45. },
  46. http.Header{
  47. "A": []string{str},
  48. "B": []string{""},
  49. "C": []string{str},
  50. "D": []string{"Sat, 01 Jan 2000 12:34:56 GMT"},
  51. },
  52. },
  53. {
  54. // slices and arrays
  55. struct {
  56. A []string
  57. B []*string
  58. C [2]string
  59. D []bool `header:",int"`
  60. }{
  61. A: []string{"a", "b"},
  62. B: []*string{&str, &str},
  63. C: [2]string{"a", "b"},
  64. D: []bool{true, false},
  65. },
  66. http.Header{
  67. "A": []string{"a", "b"},
  68. "B": {"string", "string"},
  69. "C": []string{"a", "b"},
  70. "D": {"1", "0"},
  71. },
  72. },
  73. {
  74. // other types
  75. struct {
  76. A time.Time
  77. B time.Time `header:",unix"`
  78. C bool `header:",int"`
  79. D bool `header:",int"`
  80. E http.Header
  81. }{
  82. A: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC),
  83. B: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC),
  84. C: true,
  85. D: false,
  86. E: http.Header{
  87. "F": []string{"f1"},
  88. "G": []string{"gg"},
  89. },
  90. },
  91. http.Header{
  92. "A": []string{"Sat, 01 Jan 2000 12:34:56 GMT"},
  93. "B": []string{"946730096"},
  94. "C": []string{"1"},
  95. "D": []string{"0"},
  96. "F": []string{"f1"},
  97. "G": []string{"gg"},
  98. },
  99. },
  100. {
  101. nil,
  102. http.Header{},
  103. },
  104. {
  105. &struct {
  106. A string
  107. }{"test"},
  108. http.Header{
  109. "A": []string{"test"},
  110. },
  111. },
  112. }
  113. for i, tt := range tests {
  114. v, err := Header(tt.in)
  115. if err != nil {
  116. t.Errorf("%d. Header(%q) returned error: %v", i, tt.in, err)
  117. }
  118. if !reflect.DeepEqual(tt.want, v) {
  119. t.Errorf("%d. Header(%q) returned %#v, want %#v", i, tt.in, v, tt.want)
  120. }
  121. }
  122. }
  123. func TestHeader_omitEmpty(t *testing.T) {
  124. str := ""
  125. s := struct {
  126. a string
  127. A string
  128. B string `header:",omitempty"`
  129. C string `header:"-"`
  130. D string `header:"omitempty"` // actually named omitempty, not an option
  131. E *string `header:",omitempty"`
  132. F bool `header:",omitempty"`
  133. G int `header:",omitempty"`
  134. H uint `header:",omitempty"`
  135. I float32 `header:",omitempty"`
  136. J time.Time `header:",omitempty"`
  137. K struct{} `header:",omitempty"`
  138. }{E: &str}
  139. v, err := Header(s)
  140. if err != nil {
  141. t.Errorf("Header(%#v) returned error: %v", s, err)
  142. }
  143. want := http.Header{
  144. "A": []string{""},
  145. "Omitempty": []string{""},
  146. "E": []string{""}, // E is included because the pointer is not empty, even though the string being pointed to is
  147. }
  148. if !reflect.DeepEqual(want, v) {
  149. t.Errorf("Header(%#v) returned %v, want %v", s, v, want)
  150. }
  151. }
  152. type A struct {
  153. B
  154. }
  155. type B struct {
  156. C string
  157. }
  158. type D struct {
  159. B
  160. C string
  161. }
  162. type e struct {
  163. B
  164. C string
  165. }
  166. type F struct {
  167. e
  168. }
  169. func TestHeader_embeddedStructs(t *testing.T) {
  170. tests := []struct {
  171. in interface{}
  172. want http.Header
  173. }{
  174. {
  175. A{B{C: "foo"}},
  176. http.Header{"C": []string{"foo"}},
  177. },
  178. {
  179. D{B: B{C: "bar"}, C: "foo"},
  180. http.Header{"C": []string{"foo", "bar"}},
  181. },
  182. {
  183. F{e{B: B{C: "bar"}, C: "foo"}}, // With unexported embed
  184. http.Header{"C": []string{"foo", "bar"}},
  185. },
  186. }
  187. for i, tt := range tests {
  188. v, err := Header(tt.in)
  189. if err != nil {
  190. t.Errorf("%d. Header(%q) returned error: %v", i, tt.in, err)
  191. }
  192. if !reflect.DeepEqual(tt.want, v) {
  193. t.Errorf("%d. Header(%q) returned %v, want %v", i, tt.in, v, tt.want)
  194. }
  195. }
  196. }
  197. func TestHeader_invalidInput(t *testing.T) {
  198. _, err := Header("")
  199. if err == nil {
  200. t.Errorf("expected Header() to return an error on invalid input")
  201. }
  202. }
  203. type EncodedArgs []string
  204. func (m EncodedArgs) EncodeHeader(key string, v *http.Header) error {
  205. for i, arg := range m {
  206. v.Set(fmt.Sprintf("%s.%d", key, i), arg)
  207. }
  208. return nil
  209. }
  210. func TestHeader_Marshaler(t *testing.T) {
  211. s := struct {
  212. Args EncodedArgs `header:"arg"`
  213. }{[]string{"a", "b", "c"}}
  214. v, err := Header(s)
  215. if err != nil {
  216. t.Errorf("Header(%q) returned error: %v", s, err)
  217. }
  218. want := http.Header{
  219. "Arg.0": []string{"a"},
  220. "Arg.1": []string{"b"},
  221. "Arg.2": []string{"c"},
  222. }
  223. if !reflect.DeepEqual(want, v) {
  224. t.Errorf("Header(%q) returned %v, want %v", s, v, want)
  225. }
  226. }
  227. func TestHeader_MarshalerWithNilPointer(t *testing.T) {
  228. s := struct {
  229. Args *EncodedArgs `header:"arg"`
  230. }{}
  231. v, err := Header(s)
  232. if err != nil {
  233. t.Errorf("Header(%q) returned error: %v", s, err)
  234. }
  235. want := http.Header{}
  236. if !reflect.DeepEqual(want, v) {
  237. t.Errorf("Header(%q) returned %v, want %v", s, v, want)
  238. }
  239. }
  240. func TestTagParsing(t *testing.T) {
  241. name, opts := parseTag("field,foobar,foo")
  242. if name != "field" {
  243. t.Fatalf("name = %q, want field", name)
  244. }
  245. for _, tt := range []struct {
  246. opt string
  247. want bool
  248. }{
  249. {"foobar", true},
  250. {"foo", true},
  251. {"bar", false},
  252. {"field", false},
  253. } {
  254. if opts.Contains(tt.opt) != tt.want {
  255. t.Errorf("Contains(%q) = %v", tt.opt, !tt.want)
  256. }
  257. }
  258. }