convert_test.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package queryparams_test
  14. import (
  15. "net/url"
  16. "reflect"
  17. "testing"
  18. "time"
  19. "k8s.io/kubernetes/pkg/api/unversioned"
  20. "k8s.io/kubernetes/pkg/conversion/queryparams"
  21. )
  22. type namedString string
  23. type namedBool bool
  24. type bar struct {
  25. Float1 float32 `json:"float1"`
  26. Float2 float64 `json:"float2"`
  27. Int1 int64 `json:"int1,omitempty"`
  28. Int2 int32 `json:"int2,omitempty"`
  29. Int3 int16 `json:"int3,omitempty"`
  30. Str1 string `json:"str1,omitempty"`
  31. Ignored int
  32. Ignored2 string
  33. }
  34. func (obj *bar) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
  35. type foo struct {
  36. Str string `json:"str"`
  37. Integer int `json:"integer,omitempty"`
  38. Slice []string `json:"slice,omitempty"`
  39. Boolean bool `json:"boolean,omitempty"`
  40. NamedStr namedString `json:"namedStr,omitempty"`
  41. NamedBool namedBool `json:"namedBool,omitempty"`
  42. Foobar bar `json:"foobar,omitempty"`
  43. Testmap map[string]string `json:"testmap,omitempty"`
  44. }
  45. func (obj *foo) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
  46. type baz struct {
  47. Ptr *int `json:"ptr"`
  48. Bptr *bool `json:"bptr,omitempty"`
  49. }
  50. func (obj *baz) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
  51. // childStructs tests some of the types we serialize to query params for log API calls
  52. // notably, the nested time struct
  53. type childStructs struct {
  54. Container string `json:"container,omitempty"`
  55. Follow bool `json:"follow,omitempty"`
  56. Previous bool `json:"previous,omitempty"`
  57. SinceSeconds *int64 `json:"sinceSeconds,omitempty"`
  58. SinceTime *unversioned.Time `json:"sinceTime,omitempty"`
  59. EmptyTime *unversioned.Time `json:"emptyTime"`
  60. }
  61. func (obj *childStructs) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
  62. func validateResult(t *testing.T, input interface{}, actual, expected url.Values) {
  63. local := url.Values{}
  64. for k, v := range expected {
  65. local[k] = v
  66. }
  67. for k, v := range actual {
  68. if ev, ok := local[k]; !ok || !reflect.DeepEqual(ev, v) {
  69. if !ok {
  70. t.Errorf("%#v: actual value key %s not found in expected map", input, k)
  71. } else {
  72. t.Errorf("%#v: values don't match: actual: %#v, expected: %#v", input, v, ev)
  73. }
  74. }
  75. delete(local, k)
  76. }
  77. if len(local) > 0 {
  78. t.Errorf("%#v: expected map has keys that were not found in actual map: %#v", input, local)
  79. }
  80. }
  81. func TestConvert(t *testing.T) {
  82. sinceSeconds := int64(123)
  83. sinceTime := unversioned.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)
  84. tests := []struct {
  85. input interface{}
  86. expected url.Values
  87. }{
  88. {
  89. input: &foo{
  90. Str: "hello",
  91. },
  92. expected: url.Values{"str": {"hello"}},
  93. },
  94. {
  95. input: &foo{
  96. Str: "test string",
  97. Slice: []string{"one", "two", "three"},
  98. Integer: 234,
  99. Boolean: true,
  100. },
  101. expected: url.Values{"str": {"test string"}, "slice": {"one", "two", "three"}, "integer": {"234"}, "boolean": {"true"}},
  102. },
  103. {
  104. input: &foo{
  105. Str: "named types",
  106. NamedStr: "value1",
  107. NamedBool: true,
  108. },
  109. expected: url.Values{"str": {"named types"}, "namedStr": {"value1"}, "namedBool": {"true"}},
  110. },
  111. {
  112. input: &foo{
  113. Str: "don't ignore embedded struct",
  114. Foobar: bar{
  115. Float1: 5.0,
  116. },
  117. },
  118. expected: url.Values{"str": {"don't ignore embedded struct"}, "float1": {"5"}, "float2": {"0"}},
  119. },
  120. {
  121. // Ignore untagged fields
  122. input: &bar{
  123. Float1: 23.5,
  124. Float2: 100.7,
  125. Int1: 1,
  126. Int2: 2,
  127. Int3: 3,
  128. Ignored: 1,
  129. Ignored2: "ignored",
  130. },
  131. expected: url.Values{"float1": {"23.5"}, "float2": {"100.7"}, "int1": {"1"}, "int2": {"2"}, "int3": {"3"}},
  132. },
  133. {
  134. // include fields that are not tagged omitempty
  135. input: &foo{
  136. NamedStr: "named str",
  137. },
  138. expected: url.Values{"str": {""}, "namedStr": {"named str"}},
  139. },
  140. {
  141. input: &baz{
  142. Ptr: intp(5),
  143. Bptr: boolp(true),
  144. },
  145. expected: url.Values{"ptr": {"5"}, "bptr": {"true"}},
  146. },
  147. {
  148. input: &baz{
  149. Bptr: boolp(true),
  150. },
  151. expected: url.Values{"ptr": {""}, "bptr": {"true"}},
  152. },
  153. {
  154. input: &baz{
  155. Ptr: intp(5),
  156. },
  157. expected: url.Values{"ptr": {"5"}},
  158. },
  159. {
  160. input: &childStructs{
  161. Container: "mycontainer",
  162. Follow: true,
  163. Previous: true,
  164. SinceSeconds: &sinceSeconds,
  165. SinceTime: &sinceTime, // test a custom marshaller
  166. EmptyTime: nil, // test a nil custom marshaller without omitempty
  167. },
  168. expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "sinceTime": {"2000-01-01T12:34:56Z"}, "emptyTime": {""}},
  169. },
  170. {
  171. input: &childStructs{
  172. Container: "mycontainer",
  173. Follow: true,
  174. Previous: true,
  175. SinceSeconds: &sinceSeconds,
  176. SinceTime: nil, // test a nil custom marshaller with omitempty
  177. },
  178. expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "emptyTime": {""}},
  179. },
  180. }
  181. for _, test := range tests {
  182. result, err := queryparams.Convert(test.input)
  183. if err != nil {
  184. t.Errorf("Unexpected error while converting %#v: %v", test.input, err)
  185. }
  186. validateResult(t, test.input, result, test.expected)
  187. }
  188. }
  189. func intp(n int) *int { return &n }
  190. func boolp(b bool) *bool { return &b }